Skip to content

Conversation

@dstaley
Copy link
Member

@dstaley dstaley commented Aug 14, 2025

Description

This PR implements fetchStatus for our experimental Custom Flow APIs, which are not yet intended for public use. It also refactors the methods in SignInFuture to use a runAsyncTask utility function that handles fetch status and error event emission.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features
    • Added observable fetch/loading status for Sign-In, enabling UIs to reflect idle/fetching states.
    • Unified error handling for Sign-In flows for more consistent error results.
    • Enhanced post-sign-in behavior with optional navigation support for smoother redirects.
  • Tests
    • Added coverage for async resource handling to ensure correct loading and error emissions.
  • Chores
    • Bumped minor versions for @clerk/clerk-js and @clerk/types.
    • Documented an experimental Signal-based fetchStatus feature.

@changeset-bot
Copy link

changeset-bot bot commented Aug 14, 2025

🦋 Changeset detected

Latest commit: 2336512

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
Name Type
@clerk/clerk-js Minor
@clerk/types Minor
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/elements Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/localizations Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/clerk-react Patch
@clerk/remix Patch
@clerk/shared Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/themes Patch
@clerk/vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Aug 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Aug 15, 2025 7:14pm

@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 14, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@6549

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@6549

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@6549

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@6549

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@6549

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@6549

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@6549

@clerk/clerk-expo

npm i https://pkg.pr.new/@clerk/clerk-expo@6549

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@6549

@clerk/express

npm i https://pkg.pr.new/@clerk/express@6549

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@6549

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@6549

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@6549

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@6549

@clerk/clerk-react

npm i https://pkg.pr.new/@clerk/clerk-react@6549

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@6549

@clerk/remix

npm i https://pkg.pr.new/@clerk/remix@6549

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@6549

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@6549

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@6549

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@6549

@clerk/types

npm i https://pkg.pr.new/@clerk/types@6549

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@6549

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@6549

commit: 2336512

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

📝 Walkthrough

Walkthrough

Introduces a ResourceFetch event and payload to the event system. Adds a runAsyncResourceTask utility to standardize resource task lifecycle and emissions. Refactors SignInFuture methods to use the new wrapper, removes its fetchStatus property, and updates finalize to accept an optional navigate parameter. Renames signInSignal to signInResourceSignal, adds signInFetchSignal, and updates computed signals to include fetchStatus. State now exposes signInFetchSignal and listens to resource:fetch to update it. Types are updated accordingly for SignInFutureResource and State. Adds unit tests for runAsyncResourceTask. Includes a changeset for minor version bumps and notes experimental Signal fetchStatus support.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🔭 Outside diff range comments (3)
packages/clerk-js/src/core/resources/SignIn.ts (3)

579-582: Don’t swallow errors from create() inside runAsyncTask; abort on failure

await this.create(...) returns { error } (it doesn’t throw), so failures won’t short-circuit this flow. We then proceed to read supportedFirstFactors and likely throw a secondary, misleading error. Propagate the original error to the outer runAsyncTask.

-      if (!this.resource.id) {
-        await this.create({ identifier: email });
-      }
+      if (!this.resource.id) {
+        const { error } = await this.create({ identifier: email });
+        if (error) {
+          throw error;
+        }
+      }

624-631: Same issue: propagate create() errors in SSO path

As above, ensure failures from create() abort the rest of the task; otherwise subsequent logic runs in an invalid state.

-      if (!this.resource.id) {
-        await this.create({
-          strategy,
-          redirectUrl: redirectUrlWithAuthToken,
-          actionCompleteRedirectUrl: redirectUrlComplete,
-        });
-      }
+      if (!this.resource.id) {
+        const { error } = await this.create({
+          strategy,
+          redirectUrl: redirectUrlWithAuthToken,
+          actionCompleteRedirectUrl: redirectUrlComplete,
+        });
+        if (error) {
+          throw error;
+        }
+      }

1-72: Autofix blocked by import resolution errors
Running ESLint --fix on packages/clerk-js/src/core/resources/SignIn.ts failed with import/no-unresolved errors for @clerk/shared/*, so the simple-import-sort/imports fix couldn’t be applied.

Please:

  • Ensure @clerk/shared is installed and your workspace is set up correctly.
  • Configure ESLint’s import resolver (e.g. via eslint-import-resolver-typescript) so it can resolve your TypeScript path aliases.
  • Rerun your workspace linter (pnpm -w lint --fix or equivalent) to automatically sort the imports.
🧹 Nitpick comments (10)
.changeset/twelve-crabs-return.md (1)

1-6: Confirm semver: removing a property from @clerk/types may be a breaking change

AI summary notes that fetchStatus was removed from SignInFutureResource in @clerk/types. If any external consumer references that type, this is a type-level breaking change and should be a major bump. If it’s strictly experimental and not part of the public API surface, please confirm and consider documenting that rationale here; otherwise, adjust the versioning.

You can also expand the changeset notes for discoverability:

-[Experimental] Signal `fetchStatus` support.
+[Experimental] Signal `fetchStatus` support.
+
+This introduces `resource:fetch` events and `runAsyncTask` to standardize fetch lifecycle handling.
+The sign-in computed signal now exposes `fetchStatus` ('idle' | 'fetching').
+
+Note: `fetchStatus` was removed from `SignInFutureResource` in `@clerk/types` and moved to the signal layer.
+If you were using it directly from `SignInFutureResource`, migrate to `state.signInSignal().fetchStatus`.
packages/types/src/state.ts (1)

35-36: Unify fetch status typing and update docs to reflect the new field

  • Prefer a shared type alias for fetch status to avoid drifting unions across packages.
  • Update the comment to mention the new fetchStatus field.

Apply this local diff to use a shared alias:

-      fetchStatus: 'idle' | 'fetching';
+      fetchStatus: FetchStatus;

And add this type near the top of the file to centralize the union (or export it from a shared types module if you prefer to reuse across packages):

export type FetchStatus = 'idle' | 'fetching';

Additionally, consider amending the doc comment above signInSignal to mention it returns fetchStatus alongside errors and signIn, and whether it’s experimental.

packages/clerk-js/src/core/events.ts (2)

13-14: Document the new event for clarity and future discoverability

Add JSDoc to describe when resource:fetch is emitted and its semantics.

   ResourceError: 'resource:error',
-  ResourceFetch: 'resource:fetch',
+  /**
+   * Emitted when an async task begins/ends for a resource.
+   * Semantics:
+   * - status: 'fetching' is emitted when a task starts
+   * - status: 'idle' is emitted when a task completes
+   */
+  ResourceFetch: 'resource:fetch',

19-20: Consider centralizing the fetch-status type

'idle' | 'fetching' appears in multiple places. To avoid drift, export a shared FetchStatus type from @clerk/types (e.g., packages/types/src/state.ts) and consume it here:

// Example:
import type { FetchStatus } from '@clerk/types';

export type ResourceFetchPayload = { resource: BaseResource; status: FetchStatus };

This keeps the union consistent across packages.

packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts (1)

11-11: Avoid any in tests; prefer a type-only import for the resource

Stick to type-only imports to satisfy the typing without pulling runtime code.

+import type { BaseResource } from '../../core/resources/internal';
 
-const resource = {} as any; //  runAsyncTask doesn't depend on resource being a BaseResource
+const resource = {} as unknown as BaseResource; // runAsyncTask doesn't depend on resource being a BaseResource
packages/clerk-js/src/utils/runAsyncTask.ts (1)

8-12: Optional: return a discriminated union instead of null-able error

A discriminated union improves ergonomics and avoids returning error: null on success.

Example:

export type AsyncTaskResult<T> =
  | { ok: true; result: T }
  | { ok: false; error: unknown };

export async function runAsyncTask<T>(...): Promise<AsyncTaskResult<T>> { /* ... */ }
packages/clerk-js/src/core/state.ts (2)

36-40: Avoid duplicating the fetch-status union across files

The union 'idle' | 'fetching' is hard-coded here and in signals.ts. Prefer a single exported type to prevent drift.

Apply this diff here (paired with the signals.ts change below) to reuse a shared type:

 import type { BaseResource } from './resources/Base';
+import type { FetchStatus } from './signals';

-  private onResourceFetch = (payload: { resource: BaseResource; status: 'idle' | 'fetching' }) => {
+  private onResourceFetch = (payload: { resource: BaseResource; status: FetchStatus }) => {
     if (payload.resource instanceof SignIn) {
       this.signInFetchSignal({ status: payload.status });
     }
   };

10-13: Mark signal properties as readonly (public by intent)

These are effectively public API surface on State; make them explicit and immutable to align with the guidelines and prevent accidental reassignment.

-  signInResourceSignal = signInResourceSignal;
-  signInErrorSignal = signInErrorSignal;
-  signInFetchSignal = signInFetchSignal;
-  signInSignal = signInComputedSignal;
+  public readonly signInResourceSignal = signInResourceSignal;
+  public readonly signInErrorSignal = signInErrorSignal;
+  public readonly signInFetchSignal = signInFetchSignal;
+  public readonly signInSignal = signInComputedSignal;
packages/clerk-js/src/core/resources/SignIn.ts (1)

567-575: Add unit tests for error propagation in nested flows

Now that create() returns { error }, paths that call it inside another runAsyncTask should assert that an error aborts subsequent steps (e.g., factor lookup, navigation). Add tests for:

  • sendEmailCode: failing create() causes outer runAsyncTask to emit resource:error and return { error } without attempting prepare_first_factor
  • sso: failing create() prevents reading firstFactorVerification and navigation

I can draft focused tests in utils/tests or resources/tests with a stubbed eventBus and fake __internal_basePost. Want me to open a patch with those?

Also applies to: 579-595, 617-638

packages/clerk-js/src/core/signals.ts (1)

7-10: Centralize fetch status type for reuse across modules

Export a shared type to avoid duplicating the union and improve maintainability (aligns with the paired change suggested in state.ts).

-export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });
-export const signInErrorSignal = signal<{ error: unknown }>({ error: null });
-export const signInFetchSignal = signal<{ status: 'idle' | 'fetching' }>({ status: 'idle' });
+export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });
+export const signInErrorSignal = signal<{ error: unknown }>({ error: null });
+export type FetchStatus = 'idle' | 'fetching';
+export const signInFetchSignal = signal<{ status: FetchStatus }>({ status: 'idle' });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5b24129 and e2bd52f.

📒 Files selected for processing (9)
  • .changeset/twelve-crabs-return.md (1 hunks)
  • packages/clerk-js/src/core/events.ts (2 hunks)
  • packages/clerk-js/src/core/resources/SignIn.ts (8 hunks)
  • packages/clerk-js/src/core/signals.ts (1 hunks)
  • packages/clerk-js/src/core/state.ts (3 hunks)
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts (1 hunks)
  • packages/clerk-js/src/utils/runAsyncTask.ts (1 hunks)
  • packages/types/src/signIn.ts (0 hunks)
  • packages/types/src/state.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/types/src/signIn.ts
🧰 Additional context used
📓 Path-based instructions (11)
.changeset/**

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Automated releases must use Changesets.

Files:

  • .changeset/twelve-crabs-return.md
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/src/utils/runAsyncTask.ts
  • packages/types/src/state.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
  • packages/clerk-js/src/core/events.ts
  • packages/clerk-js/src/core/signals.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/core/state.ts
packages/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Unit tests should use Jest or Vitest as the test runner.

Files:

  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Visual regression testing should be performed for UI components.

Files:

  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)

**/__tests__/**/*.{ts,tsx}: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces

Files:

  • packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts
🧬 Code Graph Analysis (5)
packages/clerk-js/src/utils/runAsyncTask.ts (1)
packages/clerk-js/src/core/events.ts (1)
  • eventBus (31-31)
packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts (2)
packages/clerk-js/src/core/events.ts (1)
  • eventBus (31-31)
packages/clerk-js/src/utils/runAsyncTask.ts (1)
  • runAsyncTask (8-30)
packages/clerk-js/src/core/signals.ts (1)
packages/clerk-js/src/core/resources/SignIn.ts (1)
  • SignIn (74-483)
packages/clerk-js/src/core/resources/SignIn.ts (1)
packages/clerk-js/src/utils/runAsyncTask.ts (1)
  • runAsyncTask (8-30)
packages/clerk-js/src/core/state.ts (4)
packages/types/src/state.ts (1)
  • State (28-58)
packages/clerk-js/src/core/signals.ts (3)
  • signInResourceSignal (7-7)
  • signInErrorSignal (8-8)
  • signInFetchSignal (9-9)
packages/clerk-js/src/core/events.ts (1)
  • eventBus (31-31)
packages/clerk-js/src/core/resources/SignIn.ts (1)
  • SignIn (74-483)
🪛 ESLint
packages/clerk-js/src/core/resources/SignIn.ts

[error] 1-72: Run autofix to sort these imports!

(simple-import-sort/imports)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Build Packages
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
packages/clerk-js/src/core/state.ts (1)

21-22: Wiring resource:fetch to fetch signal looks correct

Listening to resource:fetch and forwarding status to signInFetchSignal is the right integration point. The guard on SignIn ensures we don’t bleed fetch state across resources.

Also applies to: 36-40

packages/clerk-js/src/core/resources/SignIn.ts (1)

530-536: Refactor to runAsyncTask is clean where there’s a single API call

These methods wrap a single __internal_basePost and correctly delegate lifecycle events to runAsyncTask. No correctness issues spotted.

Also applies to: 597-604, 640-648

packages/clerk-js/src/core/signals.ts (1)

11-19: Computed shape and wiring look good

signInComputedSignal now composes resource, errors, and fetchStatus cleanly. The returned surface matches the updated types.

Comment on lines +569 to +574
} catch (err: unknown) {
eventBus.emit('resource:error', { resource: this.resource, error: err });
return { error: err };
}

return { error: null };
});
}

async sendEmailCode({ email }: { email: string }): Promise<{ error: unknown }> {
eventBus.emit('resource:error', { resource: this.resource, error: null });
try {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid sending identifier: null; include the field only when defined

Using identifier || previousIdentifier can yield null, which will serialize as identifier: null. Prefer nullish coalescing and omit the key when undefined.

-      const previousIdentifier = this.resource.identifier;
-      await this.resource.__internal_basePost({
-        path: this.resource.pathRoot,
-        body: { identifier: identifier || previousIdentifier, password },
-      });
+      const previousIdentifier = this.resource.identifier;
+      const resolvedIdentifier = identifier ?? previousIdentifier ?? undefined;
+      await this.resource.__internal_basePost({
+        path: this.resource.pathRoot,
+        body: {
+          ...(resolvedIdentifier !== undefined ? { identifier: resolvedIdentifier } : {}),
+          password,
+        },
+      });
📝 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
const previousIdentifier = this.resource.identifier;
await this.resource.__internal_basePost({
path: this.resource.pathRoot,
body: { identifier: identifier || previousIdentifier, password },
});
} catch (err: unknown) {
eventBus.emit('resource:error', { resource: this.resource, error: err });
return { error: err };
}
return { error: null };
});
const previousIdentifier = this.resource.identifier;
const resolvedIdentifier = identifier ?? previousIdentifier ?? undefined;
await this.resource.__internal_basePost({
path: this.resource.pathRoot,
body: {
...(resolvedIdentifier !== undefined ? { identifier: resolvedIdentifier } : {}),
password,
},
});
});
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/SignIn.ts around lines 569 to 574,
replace the identifier || previousIdentifier pattern (which can produce
identifier: null) with logic that uses nullish coalescing and only adds the
identifier key when defined; for example, compute const id = identifier ??
previousIdentifier and build the request body by always including password but
only adding identifier if id !== undefined (and/or !== null if you want to avoid
null explicitly), then pass that body to __internal_basePost so identifier is
omitted from the serialized payload when not defined.

Comment on lines 13 to 65
it('emits fetching/idle and returns result on success', async () => {
const emitSpy = vi.spyOn(eventBus, 'emit');
const task = vi.fn().mockResolvedValue('ok');

const { result, error } = await runAsyncTask(resource, task);

expect(task).toHaveBeenCalledTimes(1);
expect(result).toBe('ok');
expect(error).toBeNull();

expect(emitSpy).toHaveBeenNthCalledWith(1, 'resource:error', {
resource,
error: null,
});
expect(emitSpy).toHaveBeenNthCalledWith(2, 'resource:fetch', {
resource,
status: 'fetching',
});
expect(emitSpy).toHaveBeenNthCalledWith(3, 'resource:fetch', {
resource,
status: 'idle',
});
});

it('emits error and returns error on failure', async () => {
const emitSpy = vi.spyOn(eventBus, 'emit');
const thrown = new Error('fail');
const task = vi.fn().mockRejectedValue(thrown);

const { result, error } = await runAsyncTask(resource, task);

expect(task).toHaveBeenCalledTimes(1);
expect(result).toBeUndefined();
expect(error).toBe(thrown);

expect(emitSpy).toHaveBeenNthCalledWith(1, 'resource:error', {
resource,
error: null,
});
expect(emitSpy).toHaveBeenNthCalledWith(2, 'resource:fetch', {
resource,
status: 'fetching',
});
expect(emitSpy).toHaveBeenNthCalledWith(3, 'resource:error', {
resource,
error: thrown,
});
expect(emitSpy).toHaveBeenNthCalledWith(4, 'resource:fetch', {
resource,
status: 'idle',
});
});
});
Copy link
Contributor

@coderabbitai coderabbitai bot Aug 14, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Add a concurrency test to catch in-flight status race conditions

If two tasks run concurrently for the same resource, the current approach can emit idle while another task is still fetching, leading to flickering fetchStatus. Add a test that runs two overlapping tasks and asserts that resource:fetch does not flip to idle until both complete (or decide on desired semantics).

Example test to add:

it('handles overlapping tasks without prematurely emitting idle', async () => {
  const emitSpy = vi.spyOn(eventBus, 'emit');
  let resolveA: (v: unknown) => void;
  const taskA = vi.fn(() => new Promise(res => (resolveA = res)));
  const taskB = vi.fn().mockResolvedValue('ok');

  // Start A (fetching)
  const pA = runAsyncTask(resource, taskA);

  // Start B (still fetching)
  const pB = runAsyncTask(resource, taskB);

  // Resolve B first
  await pB;

  // At this point, we expect no final 'idle' yet if A is still in flight
  // Adjust assertion based on desired semantics; e.g., last call isn't 'idle' until A resolves
  const lastCall = emitSpy.mock.calls[emitSpy.mock.calls.length - 1];
  expect(lastCall[0]).toBe('resource:fetch');
  expect(lastCall[1]).toMatchObject({ resource, status: 'fetching' });

  // Now resolve A; expect idle
  resolveA!('ok');
  await pA;
  const finalCall = emitSpy.mock.calls[emitSpy.mock.calls.length - 1];
  expect(finalCall).toEqual(['resource:fetch', { resource, status: 'idle' }]);
});

🏁 Script executed:

#!/bin/bash
# Locate the implementation of runAsyncTask to verify concurrency handling
rg -n "export async function runAsyncTask" -C8 packages/clerk-js/src/utils

Length of output: 1346


Enhance runAsyncTask to handle overlapping tasks without premature “idle” emits

The current implementation in packages/clerk-js/src/utils/runAsyncTask.ts always emits resource:fetchidle as soon as any task resolves, even if other tasks for the same resource are still in flight. This will cause flickering of the fetch status when two tasks overlap.

To address this, you should:

• Introduce an in-flight counter (e.g. a WeakMap<BaseResource, number>) inside runAsyncTask:
– Increment the counter before calling await task().
– Decrement in a finally block.
– Only emit { status: 'idle' } once the counter reaches zero.

• Add a concurrency test in packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts to verify that overlapping tasks don’t flip back to idle until all tasks complete. For example:

it('does not emit idle until all overlapping tasks complete', async () => {
  const emitSpy = vi.spyOn(eventBus, 'emit');
  let resolveA!: (value: unknown) => void;
  const taskA = vi.fn(() => new Promise(res => (resolveA = res)));
  const taskB = vi.fn().mockResolvedValue('ok');

  const pA = runAsyncTask(resource, taskA);
  const pB = runAsyncTask(resource, taskB);
  await pB;

  // Should still be 'fetching' because A is not done
  const last = emitSpy.mock.calls.at(-1)!;
  expect(last).toEqual(['resource:fetch', { resource, status: 'fetching' }]);

  resolveA('ok');
  await pA;

  // Now finally emit 'idle'
  const final = emitSpy.mock.calls.at(-1)!;
  expect(final).toEqual(['resource:fetch', { resource, status: 'idle' }]);
});

Copy link
Member

Choose a reason for hiding this comment

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

@dstaley I'm not sure if this will happen in practice, but it's an interesting case to think about. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member Author

Choose a reason for hiding this comment

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

technically yes, but I don't think there's a valid instance in which it could happen. You would need to trigger two async methods on the signIn instance at one time, and I'm not sure there's a legitimate case where you'd want that to work as intended. Could be a nice refactor in the future though, but probably YAGNI for now.

Comment on lines 1 to 30
import { eventBus } from '../core/events';
import type { BaseResource } from '../core/resources/internal';

/**
* Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom
* Flow APIs.
*/
export async function runAsyncTask<T>(
resource: BaseResource,
task: () => Promise<T>,
): Promise<{ result?: T; error: unknown }> {
eventBus.emit('resource:error', { resource, error: null });
eventBus.emit('resource:fetch', {
resource,
status: 'fetching',
});

try {
const result = await task();
return { result, error: null };
} catch (err) {
eventBus.emit('resource:error', { resource, error: err });
return { error: err };
} finally {
eventBus.emit('resource:fetch', {
resource,
status: 'idle',
});
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Aug 14, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use event constants and guard against overlapping task races with in-flight counting

  • Replace string event names with events.* for consistency and type-safety.
  • Track an in-flight counter per resource so idle isn’t emitted until all concurrent tasks complete. This prevents flickering fetchStatus in UIs when overlapping tasks occur.

Apply:

-import { eventBus } from '../core/events';
+import { events, eventBus } from '../core/events';
 import type { BaseResource } from '../core/resources/internal';
 
 /**
  * Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom
  * Flow APIs.
  */
 export async function runAsyncTask<T>(
   resource: BaseResource,
   task: () => Promise<T>,
 ): Promise<{ result?: T; error: unknown }> {
-  eventBus.emit('resource:error', { resource, error: null });
-  eventBus.emit('resource:fetch', {
-    resource,
-    status: 'fetching',
-  });
+  // Keep a per-resource in-flight counter to avoid premature 'idle' on overlapping tasks.
+  // WeakMap ensures resources can be GC'd when no longer referenced.
+  const inflight: WeakMap<BaseResource, number> =
+    // Reuse a shared map on the eventBus instance to avoid module duplication across bundles/tests.
+    // @ts-expect-error: attach internal state
+    (eventBus.__inflight ??= new WeakMap<BaseResource, number>());
+
+  const inc = (inflight.get(resource) ?? 0) + 1;
+  inflight.set(resource, inc);
+
+  eventBus.emit(events.ResourceError, { resource, error: null });
+  if (inc === 1) {
+    eventBus.emit(events.ResourceFetch, {
+      resource,
+      status: 'fetching',
+    });
+  }
 
   try {
     const result = await task();
     return { result, error: null };
   } catch (err) {
-    eventBus.emit('resource:error', { resource, error: err });
+    eventBus.emit(events.ResourceError, { resource, error: err });
     return { error: err };
   } finally {
-    eventBus.emit('resource:fetch', {
-      resource,
-      status: 'idle',
-    });
+    const dec = (inflight.get(resource) ?? 1) - 1;
+    if (dec <= 0) {
+      inflight.delete(resource);
+      eventBus.emit(events.ResourceFetch, {
+        resource,
+        status: 'idle',
+      });
+    } else {
+      inflight.set(resource, dec);
+    }
   }
 }

Note: If you’d rather not attach internal state to eventBus, hoist the WeakMap to module scope.

📝 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
import { eventBus } from '../core/events';
import type { BaseResource } from '../core/resources/internal';
/**
* Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom
* Flow APIs.
*/
export async function runAsyncTask<T>(
resource: BaseResource,
task: () => Promise<T>,
): Promise<{ result?: T; error: unknown }> {
eventBus.emit('resource:error', { resource, error: null });
eventBus.emit('resource:fetch', {
resource,
status: 'fetching',
});
try {
const result = await task();
return { result, error: null };
} catch (err) {
eventBus.emit('resource:error', { resource, error: err });
return { error: err };
} finally {
eventBus.emit('resource:fetch', {
resource,
status: 'idle',
});
}
}
import { events, eventBus } from '../core/events';
import type { BaseResource } from '../core/resources/internal';
/**
* Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom
* Flow APIs.
*/
export async function runAsyncTask<T>(
resource: BaseResource,
task: () => Promise<T>,
): Promise<{ result?: T; error: unknown }> {
// Keep a per-resource in-flight counter to avoid premature 'idle' on overlapping tasks.
// WeakMap ensures resources can be GC'd when no longer referenced.
const inflight: WeakMap<BaseResource, number> =
// Reuse a shared map on the eventBus instance to avoid module duplication across bundles/tests.
// @ts-expect-error: attach internal state
(eventBus.__inflight ??= new WeakMap<BaseResource, number>());
const inc = (inflight.get(resource) ?? 0) + 1;
inflight.set(resource, inc);
eventBus.emit(events.ResourceError, { resource, error: null });
if (inc === 1) {
eventBus.emit(events.ResourceFetch, {
resource,
status: 'fetching',
});
}
try {
const result = await task();
return { result, error: null };
} catch (err) {
eventBus.emit(events.ResourceError, { resource, error: err });
return { error: err };
} finally {
const dec = (inflight.get(resource) ?? 1) - 1;
if (dec <= 0) {
inflight.delete(resource);
eventBus.emit(events.ResourceFetch, {
resource,
status: 'idle',
});
} else {
inflight.set(resource, dec);
}
}
}

Copy link
Member

Choose a reason for hiding this comment

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

@dstaley this is an interesting suggestion...do you think this could happen in practice?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member Author

Choose a reason for hiding this comment

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

same thoughts as #6549 (comment)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (3)
packages/clerk-js/src/core/resources/SignIn.ts (3)

618-639: Inline create call to avoid nested runAsyncTask; add else-branch for invalid status.

  • Avoid nested runAsyncTask by calling __internal_basePost directly.
  • Mirror the stricter error handling used elsewhere (authenticateWithRedirectOrPopup) when status is not 'unverified' or URL missing.

Apply:

-      if (!this.resource.id) {
-        await this.create({
-          strategy,
-          redirectUrl: redirectUrlWithAuthToken,
-          actionCompleteRedirectUrl: redirectUrlComplete,
-        });
-      }
+      if (!this.resource.id) {
+        await this.resource.__internal_basePost({
+          path: this.resource.pathRoot,
+          body: {
+            strategy,
+            redirectUrl: redirectUrlWithAuthToken,
+            actionCompleteRedirectUrl: redirectUrlComplete,
+          },
+        });
+      }
@@
-      if (status === 'unverified' && externalVerificationRedirectURL) {
-        windowNavigate(externalVerificationRedirectURL);
-      }
+      if (status === 'unverified' && externalVerificationRedirectURL) {
+        windowNavigate(externalVerificationRedirectURL);
+      } else {
+        clerkInvalidFAPIResponse(status, SignIn.fapiClient.buildEmailAddress('support'));
+      }

579-595: Avoid nested runAsyncTask in sendEmailCode

Replacing await this.create(...) (which itself wraps a runAsyncTask) with a direct __internal_basePost call prevents duplicate resource:fetch/idle emissions.

• File: packages/clerk-js/src/core/resources/SignIn.ts
Method: sendEmailCode (lines 579–595)

Apply this diff:

@@ packages/clerk-js/src/core/resources/SignIn.ts:579-582
-      if (!this.resource.id) {
-        await this.create({ identifier: email });
-      }
+      if (!this.resource.id) {
+        await this.resource.__internal_basePost({
+          path: this.resource.pathRoot,
+          body: { identifier: email },
+        });
+      }

1-73: Sort imports in SignIn.ts to satisfy simple-import-sort/imports

The import of runAsyncTask is out of place—it needs to be grouped with the other ../../utils/* imports and all relative imports must be alphabetized. Please reorder the imports (or run your linter’s autofix once the tooling is working) as in the example below:

Affected file:
packages/clerk-js/src/core/resources/SignIn.ts

Example diff:

 import { createValidatePassword } from '../../utils/passwords/password';
+import { runAsyncTask } from '../../utils/runAsyncTask';
 import {
   clerkInvalidFAPIResponse,
   clerkInvalidStrategy,
   clerkMissingOptionError,
   clerkMissingWebAuthnPublicKeyOptions,
   clerkVerifyEmailAddressCalledBeforeCreate,
   clerkVerifyPasskeyCalledBeforeCreate,
   clerkVerifyWeb3WalletCalledBeforeCreate,
 } from '../errors';
 import { eventBus } from '../events';
 import { BaseResource, UserData, Verification } from './internal';

• Move the runAsyncTask import up to join the other ../../utils/* imports
• Ensure all relative imports (../../…, ../…, ./…) are in ascending alphabetical order

♻️ Duplicate comments (1)
packages/clerk-js/src/core/resources/SignIn.ts (1)

569-575: Avoid serializing identifier: null in password() body.

Using identifier || previousIdentifier can yield null, resulting in identifier: null being sent. Prefer nullish coalescing and omit the key when unresolved.

Apply:

-      const previousIdentifier = this.resource.identifier;
-      await this.resource.__internal_basePost({
-        path: this.resource.pathRoot,
-        body: { identifier: identifier || previousIdentifier, password },
-      });
+      const previousIdentifier = this.resource.identifier;
+      const resolvedIdentifier = identifier ?? previousIdentifier ?? undefined;
+      await this.resource.__internal_basePost({
+        path: this.resource.pathRoot,
+        body: {
+          ...(resolvedIdentifier != null ? { identifier: resolvedIdentifier } : {}),
+          password,
+        },
+      });
🧹 Nitpick comments (1)
packages/clerk-js/src/core/resources/SignIn.ts (1)

641-648: Optionally omit undefined navigate to keep payload minimal.

setActive likely tolerates navigate: undefined, but you can elide it when not provided.

Apply:

-      await SignIn.clerk.setActive({ session: this.resource.createdSessionId, navigate });
+      await SignIn.clerk.setActive({
+        session: this.resource.createdSessionId,
+        ...(navigate ? { navigate } : {}),
+      });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e2bd52f and 5164243.

📒 Files selected for processing (2)
  • packages/clerk-js/src/core/resources/SignIn.ts (9 hunks)
  • packages/types/src/signIn.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/types/src/signIn.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
🧬 Code Graph Analysis (1)
packages/clerk-js/src/core/resources/SignIn.ts (2)
packages/clerk-js/src/utils/runAsyncTask.ts (1)
  • runAsyncTask (8-30)
packages/types/src/clerk.ts (1)
  • SetActiveNavigate (123-123)
🪛 ESLint
packages/clerk-js/src/core/resources/SignIn.ts

[error] 1-73: Run autofix to sort these imports!

(simple-import-sort/imports)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (vue, chrome)
  • GitHub Check: Integration Tests (tanstack-react-router, chrome)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Integration Tests (express, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (tanstack-react-start, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome)
  • GitHub Check: Publish with pkg-pr-new
  • GitHub Check: Unit Tests (22, **)
  • GitHub Check: Unit Tests (18, --filter=@clerk/astro --filter=@clerk/backend --filter=@clerk/express --filter=@c...
  • GitHub Check: Static analysis
🔇 Additional comments (5)
packages/clerk-js/src/core/resources/SignIn.ts (5)

509-527: LGTM: reset password email code handled via runAsyncTask.

Good guard on missing sign-in and clear factor lookup before preparing the first factor.


531-536: LGTM: verification of reset password email code centralized via runAsyncTask.

Concise and consistent with the new async handling pattern.


546-551: LGTM: submit reset password wrapped with runAsyncTask.

Defaulting signOutOfOtherSessions to true is sensible.


560-565: LGTM: create() migration to runAsyncTask.

Matches the new event-driven fetch status flow.


599-604: LGTM: email code verification via runAsyncTask.

Consistent usage of the centralized async task wrapper.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/clerk-js/src/core/signals.ts (4)

9-9: Avoid duplicating the 'idle' | 'fetching' literal union; centralize the type

Defining the fetch status union inline here risks drift if more statuses are added via events/types. Prefer a shared type alias (ideally from @clerk/types); if not available, at least localize it.

-export const signInFetchSignal = signal<{ status: 'idle' | 'fetching' }>({ status: 'idle' });
+type FetchStatus = 'idle' | 'fetching';
+export const signInFetchSignal = signal<{ status: FetchStatus }>({ status: 'idle' });

If a canonical type already exists (e.g., ResourceFetchStatus), import and use that instead to ensure consistency across the codebase.


11-19: Stable return shape and fetchStatus propagation look good; avoid returning undefined for signIn

Nice improvement keeping the return shape stable and surfacing fetchStatus. Minor nit: if __internal_future can ever be undefined, the current expression may return undefined instead of null. Consider nullish coalescing.

-  return { errors, fetchStatus, signIn: signIn ? signIn.__internal_future : null };
+  return { errors, fetchStatus, signIn: signIn?.__internal_future ?? null };

Additionally, consider adding/confirming a unit test that:

  • Asserts fetchStatus is 'idle' by default and transitions to 'fetching' when the corresponding event is emitted.
  • Asserts errors is the empty parsed shape when error is null.

43-45: Use nullish check instead of generic falsy check to avoid swallowing legitimate values

This fixes the prior null handling—thanks. To be precise, prefer error == null to only skip null/undefined and not other falsy values.

-  if (!error) {
+  if (error == null) {
     return parsedErrors;
   }

55-95: Avoid shadowing the outer error variable inside forEach

The parameter name error in the forEach callback shadows the outer error identifier, which can be confusing and may trip lint rules (no-shadow). Rename it for clarity.

-  error.errors.forEach(error => {
-    if ('meta' in error && error.meta && 'paramName' in error.meta) {
-      switch (error.meta.paramName) {
+  error.errors.forEach(apiError => {
+    if ('meta' in apiError && apiError.meta && 'paramName' in apiError.meta) {
+      switch (apiError.meta.paramName) {
         case 'first_name':
-          parsedErrors.fields.firstName = error;
+          parsedErrors.fields.firstName = apiError;
           break;
         case 'last_name':
-          parsedErrors.fields.lastName = error;
+          parsedErrors.fields.lastName = apiError;
           break;
         case 'email_address':
-          parsedErrors.fields.emailAddress = error;
+          parsedErrors.fields.emailAddress = apiError;
           break;
         case 'identifier':
-          parsedErrors.fields.identifier = error;
+          parsedErrors.fields.identifier = apiError;
           break;
         case 'phone_number':
-          parsedErrors.fields.phoneNumber = error;
+          parsedErrors.fields.phoneNumber = apiError;
           break;
         case 'password':
-          parsedErrors.fields.password = error;
+          parsedErrors.fields.password = apiError;
           break;
         case 'username':
-          parsedErrors.fields.username = error;
+          parsedErrors.fields.username = apiError;
           break;
         case 'code':
-          parsedErrors.fields.code = error;
+          parsedErrors.fields.code = apiError;
           break;
         case 'captcha':
-          parsedErrors.fields.captcha = error;
+          parsedErrors.fields.captcha = apiError;
           break;
         case 'legal_accepted':
-          parsedErrors.fields.legalAccepted = error;
+          parsedErrors.fields.legalAccepted = apiError;
           break;
         default:
-          parsedErrors.global.push(error);
+          parsedErrors.global.push(apiError);
           break;
       }
     } else {
-      parsedErrors.global.push(error);
+      parsedErrors.global.push(apiError);
     }
   });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5164243 and d7f905a.

📒 Files selected for processing (1)
  • packages/clerk-js/src/core/signals.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/core/signals.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/core/signals.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/core/signals.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/core/signals.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/core/signals.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/core/signals.ts
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/src/core/signals.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Unit Tests (22, **)

import type { SignIn } from './resources/SignIn';

export const signInSignal = signal<{ resource: SignIn | null }>({ resource: null });
export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });
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

Rename to signInResourceSignal is clear; verify callers and mark as internal

Good separation of responsibilities by renaming to signInResourceSignal. If this is not part of the public API, add an @internal JSDoc to comply with docs guidelines and prevent accidental external usage. Also, please verify there are no lingering imports/usages of the old signInSignal.

Run this to ensure no leftover references exist:

Apply this diff to mark the export as internal:

+/** @internal */
 export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Find any remaining references to the old name
rg -n --heading '\bsignInSignal\b' -g '!**/node_modules/**'

Length of output: 365


Update leftover signInSignal references & mark new export as internal

We’ve got three remaining signInSignal references that need to be switched over (and the new export marked @internal):

  • packages/clerk-js/src/core/signals.ts

    +/** @internal */
     export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });
  • packages/clerk-js/src/core/state.ts (line 13)

    -export const signInSignal = signInComputedSignal;
    +export const signInResourceSignal = signInComputedSignal;
  • packages/types/src/state.ts (line 32)

    -signInSignal: Signal<SignIn | null>;
    +signInResourceSignal: Signal<SignIn | null>;
  • packages/react/src/hooks/useClerkSignal.ts (lines 21 & 38)

    -clerk.__internal_state!.signInSignal();
    +clerk.__internal_state!.signInResourceSignal();
    
    -return clerk.__internal_state.signInSignal();
    +return clerk.__internal_state.signInResourceSignal();

Please make these changes to fully remove the old signInSignal and ensure the new export is marked @internal.

📝 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
export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });
/** @internal */
export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null });
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/signals.ts around line 7, mark the new export
signInResourceSignal as internal by adding a JSDoc @internal comment immediately
above its declaration; then update the three remaining references of the old
signInSignal to the new name across the codebase: change usages in
packages/clerk-js/src/core/state.ts (line 13), packages/types/src/state.ts (line
32), and packages/react/src/hooks/useClerkSignal.ts (lines 21 and 38) to import
and use signInResourceSignal instead of signInSignal, ensuring any type
imports/exports are updated accordingly so imports resolve and there are no
leftover references to signInSignal.

* Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom
* Flow APIs.
*/
export async function runAsyncTask<T>(
Copy link
Member

Choose a reason for hiding this comment

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

🙃 Should this be runAsyncResourceTask or something instead? The name implies it being relatively generic, but that's not the case

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, lemme think on a new name. I was hoping I could co-locate this in a spot that made it obvious that it was for the custom flows implementation, but there's not really a good spot that makes that clear.

Comment on lines 1 to 30
import { eventBus } from '../core/events';
import type { BaseResource } from '../core/resources/internal';

/**
* Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom
* Flow APIs.
*/
export async function runAsyncTask<T>(
resource: BaseResource,
task: () => Promise<T>,
): Promise<{ result?: T; error: unknown }> {
eventBus.emit('resource:error', { resource, error: null });
eventBus.emit('resource:fetch', {
resource,
status: 'fetching',
});

try {
const result = await task();
return { result, error: null };
} catch (err) {
eventBus.emit('resource:error', { resource, error: err });
return { error: err };
} finally {
eventBus.emit('resource:fetch', {
resource,
status: 'idle',
});
}
}
Copy link
Member

Choose a reason for hiding this comment

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

@dstaley this is an interesting suggestion...do you think this could happen in practice?

Comment on lines 13 to 65
it('emits fetching/idle and returns result on success', async () => {
const emitSpy = vi.spyOn(eventBus, 'emit');
const task = vi.fn().mockResolvedValue('ok');

const { result, error } = await runAsyncTask(resource, task);

expect(task).toHaveBeenCalledTimes(1);
expect(result).toBe('ok');
expect(error).toBeNull();

expect(emitSpy).toHaveBeenNthCalledWith(1, 'resource:error', {
resource,
error: null,
});
expect(emitSpy).toHaveBeenNthCalledWith(2, 'resource:fetch', {
resource,
status: 'fetching',
});
expect(emitSpy).toHaveBeenNthCalledWith(3, 'resource:fetch', {
resource,
status: 'idle',
});
});

it('emits error and returns error on failure', async () => {
const emitSpy = vi.spyOn(eventBus, 'emit');
const thrown = new Error('fail');
const task = vi.fn().mockRejectedValue(thrown);

const { result, error } = await runAsyncTask(resource, task);

expect(task).toHaveBeenCalledTimes(1);
expect(result).toBeUndefined();
expect(error).toBe(thrown);

expect(emitSpy).toHaveBeenNthCalledWith(1, 'resource:error', {
resource,
error: null,
});
expect(emitSpy).toHaveBeenNthCalledWith(2, 'resource:fetch', {
resource,
status: 'fetching',
});
expect(emitSpy).toHaveBeenNthCalledWith(3, 'resource:error', {
resource,
error: thrown,
});
expect(emitSpy).toHaveBeenNthCalledWith(4, 'resource:fetch', {
resource,
status: 'idle',
});
});
});
Copy link
Member

Choose a reason for hiding this comment

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

@dstaley I'm not sure if this will happen in practice, but it's an interesting case to think about. WDYT?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (2)
packages/clerk-js/src/core/resources/SignIn.ts (2)

579-596: Avoid nested runAsyncResourceTask calls to prevent status flicker

Calling this.create(...) here triggers another runAsyncResourceTask within an existing one, producing multiple fetch/error emissions for a single logical operation. Inline the base post to keep a single lifecycle.

-      if (!this.resource.id) {
-        await this.create({ identifier: email });
-      }
+      if (!this.resource.id) {
+        await this.resource.__internal_basePost({
+          path: this.resource.pathRoot,
+          body: { identifier: email },
+        });
+      }

618-639: Handle unexpected verification state like the non-future API does

Mirror authenticateWithRedirectOrPopup by surfacing an explicit error if the state is not unverified with a URL. This provides clear feedback and consistency.

       if (status === 'unverified' && externalVerificationRedirectURL) {
         windowNavigate(externalVerificationRedirectURL);
-      }
+      } else {
+        // Stay consistent with authenticateWithRedirectOrPopup
+        clerkInvalidFAPIResponse(status, SignIn.fapiClient.buildEmailAddress('support'));
+      }

Additionally, consider inlining the initial create request (like the sendEmailCode suggestion) to avoid nested wrappers.

♻️ Duplicate comments (1)
packages/clerk-js/src/core/resources/SignIn.ts (1)

569-575: Avoid sending identifier: null; omit when not provided

Using identifier || previousIdentifier can yield identifier: null, which will be serialized and sent. Use nullish coalescing and conditionally include the key.

-    return runAsyncResourceTask(this.resource, async () => {
-      const previousIdentifier = this.resource.identifier;
-      await this.resource.__internal_basePost({
-        path: this.resource.pathRoot,
-        body: { identifier: identifier || previousIdentifier, password },
-      });
-    });
+    return runAsyncResourceTask(this.resource, async () => {
+      const previousIdentifier = this.resource.identifier;
+      const resolvedIdentifier = identifier ?? previousIdentifier ?? undefined;
+      await this.resource.__internal_basePost({
+        path: this.resource.pathRoot,
+        body: {
+          ...(resolvedIdentifier !== undefined ? { identifier: resolvedIdentifier } : {}),
+          password,
+        },
+      });
+    });
🧹 Nitpick comments (6)
packages/clerk-js/src/utils/runAsyncResourceTask.ts (2)

12-29: Potentially incorrect fetchStatus with overlapping tasks

This emits a fetch "fetching" at start and "idle" in finally for every task. With overlapping tasks on the same resource, one task finishing will flip status to idle even if others are still in-flight, leading to flicker or stale status. Consider either:

  • Enriching the payload with a requestId to let consumers track active tasks, or
  • Maintaining a per-resource in-flight counter (e.g., WeakMap) and emitting "idle" only when the counter hits 0.

If you keep the current semantics, please confirm that consumers treat fetch events correctly in presence of concurrency.


4-7: JSDoc: Document event semantics and ordering

Add a short note that the function emits:

  1. resource:error with error: null, then
  2. resource:fetch with status: 'fetching',
  3. resource:error on failure, and
  4. resource:fetch with status: 'idle' in finally.

This will help future consumers and test writers.

packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts (2)

6-6: Align test name with the function under test

Minor naming mismatch; improves clarity in test reports.

-describe('runAsyncTask', () => {
+describe('runAsyncResourceTask', () => {

11-11: Avoid any in tests; use a typed stub

Keep tests type-safe. Import the type and use a minimal stub.

-  const resource = {} as any; //  runAsyncTask doesn't depend on resource being a BaseResource
+  import type { BaseResource } from '../../core/resources/internal';
+  // runAsyncResourceTask doesn't depend on resource shape beyond identity; a minimal stub is sufficient
+  const resource = {} as unknown as BaseResource;
packages/clerk-js/src/core/resources/SignIn.ts (2)

509-528: Prefer consistent error helpers over raw Error

Use existing Clerk error helpers for consistency (e.g., clerkVerify... or a dedicated error builder) instead of generic new Error(...). This aligns error codes/messages and improves DX.


508-649: Add tests for SignInFuture wrappers’ fetchStatus/error emissions

You added focused tests for the utility, which is great. Given the refactor, please add unit/integration tests for a representative subset of SignInFuture methods (e.g., password, sendEmailCode, sso, finalize) to assert:

  • Emission ordering for resource:error and resource:fetch
  • Error propagation in returned { error }
  • No duplicate or unexpected emissions (especially avoiding nested wrapper double-emits if you keep the current structure)

I can help scaffold these tests if useful.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d99ed16 and 37b024d.

📒 Files selected for processing (3)
  • packages/clerk-js/src/core/resources/SignIn.ts (9 hunks)
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts (1 hunks)
  • packages/clerk-js/src/utils/runAsyncResourceTask.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/src/utils/runAsyncResourceTask.ts
  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Unit tests should use Jest or Vitest as the test runner.

Files:

  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Visual regression testing should be performed for UI components.

Files:

  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)

**/__tests__/**/*.{ts,tsx}: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces

Files:

  • packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts
🧬 Code Graph Analysis (3)
packages/clerk-js/src/utils/runAsyncResourceTask.ts (1)
packages/clerk-js/src/core/events.ts (1)
  • eventBus (31-31)
packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts (2)
packages/clerk-js/src/core/events.ts (1)
  • eventBus (31-31)
packages/clerk-js/src/utils/runAsyncResourceTask.ts (1)
  • runAsyncResourceTask (8-30)
packages/clerk-js/src/core/resources/SignIn.ts (2)
packages/clerk-js/src/utils/runAsyncResourceTask.ts (1)
  • runAsyncResourceTask (8-30)
packages/types/src/clerk.ts (1)
  • SetActiveNavigate (123-123)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
packages/clerk-js/src/utils/runAsyncResourceTask.ts (1)

8-12: Return type should reflect that error can be null

The implementation always returns error: null on success. Tighten the return type accordingly to improve type safety for consumers.

-export async function runAsyncResourceTask<T>(
-  resource: BaseResource,
-  task: () => Promise<T>,
-): Promise<{ result?: T; error: unknown }> {
+export async function runAsyncResourceTask<T>(
+  resource: BaseResource,
+  task: () => Promise<T>,
+): Promise<{ result?: T; error: unknown | null }> {

Likely an incorrect or invalid review comment.

packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts (2)

13-35: LGTM: success path covers emissions and return shape

Good coverage of emission order and result/error assertions.


37-64: LGTM: failure path validates error propagation and emissions

The test accurately asserts emission order and returned error.

packages/clerk-js/src/core/resources/SignIn.ts (6)

62-62: LGTM: centralized wrapper import

Importing runAsyncResourceTask here to standardize lifecycle handling is the right direction.


531-537: LGTM: verification wrapper and action look correct


546-552: LGTM: reset password submission wrapped correctly


560-566: LGTM: create flow standardized via wrapper


599-605: LGTM: email code verification routed via wrapper


641-649: LGTM: finalize supports navigate and standardizes lifecycle

Passing navigate into setActive is a clean extension for custom flows.

@dstaley dstaley merged commit 7b7eb1f into main Aug 18, 2025
37 checks passed
@dstaley dstaley deleted the ds.feat/signals-fetch-status branch August 18, 2025 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants