Skip to content

feat(clerk-js): Introduce navigate for setActive #6486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: main
Choose a base branch
from

Conversation

LauraBeatris
Copy link
Member

@LauraBeatris LauraBeatris commented Aug 7, 2025

Description

This PR introduces a new API to navigate on setActive, that can be leveraged for after-auth flows to handle pending sessions, featuring a callback mechanism that enables developers to integrate their application logic, such as navigation.

Previously, our flows were mistakenly navigating after setActive, which could cause issues with session revalidation and re-render of pages.

From now on, we're proposing two options to deal with after-auth: taskUrls or navigate

navigate

A callback function that can be provided as an setActive option.

setActive({ 
  navigate: async ({ session }) => { 
     const currentTask = session.currentTask; 

     if (currentTask){ 
      // Developer has full control over navigation logic
      // (this also allows our AIO components to pass their navigation context such as modal routing)
       await router.push(`/onboarding/${currentTask.key}`)
       return 
     }

    await router.push('/dashboard')
  }
})

taskUrls (existing)

A map of URLs per session task key that can be provided as a Clerk option. On setActive, it'll be used for navigation.

<ClerkProvider 
   taskUrls={{'choose-organization': '/onboarding/choose-organization' }}
/>

AIOs behavior

CleanShot.2025-08-07.at.22.30.57.mp4

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

    • Public per-task routing endpoints and a new setActive navigate callback enabling customizable post-auth and session-switch navigation; UI contexts expose task-aware navigation helpers and computed task URLs.
    • Added HOCs to support task-based redirects in sign-in/sign-up flows.
  • Breaking Changes

    • Several control components and exports renamed (Task → Tasks); setActive now accepts a navigate callback; internal navigate-to-task APIs removed.
  • Tests

    • Expanded tests for pending-session activation and navigation flows.
  • Chores

    • Updated bundle size thresholds.

@LauraBeatris LauraBeatris self-assigned this Aug 7, 2025
Copy link

changeset-bot bot commented Aug 7, 2025

🦋 Changeset detected

Latest commit: fa3a908

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/tanstack-react-start Minor
@clerk/react-router Minor
@clerk/nextjs Minor
@clerk/clerk-react Minor
@clerk/remix Minor
@clerk/vue 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/nuxt Patch
@clerk/shared Patch
@clerk/testing Patch
@clerk/themes 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

Copy link

vercel bot commented Aug 7, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Preview Comment Aug 12, 2025 2:29pm

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: 3

♻️ Duplicate comments (12)
integration/tests/session-tasks-sign-up.test.ts (2)

37-46: Guard against missing OAuth provider instance keys in teardown

createClerkClient will throw if instanceKeys.get('oauth-provider') is undefined or has missing keys. Add a guard to avoid teardown failures in CI matrices without those secrets.

Apply this diff:

-      const client = createClerkClient({
-        secretKey: instanceKeys.get('oauth-provider').sk,
-        publishableKey: instanceKeys.get('oauth-provider').pk,
-      });
-      const users = createUserService(client);
-      await users.deleteIfExists({ email: fakeUserForOAuth.email });
+      const oauthKeys = instanceKeys.get('oauth-provider');
+      if (oauthKeys?.sk && oauthKeys?.pk) {
+        const client = createClerkClient({
+          secretKey: oauthKeys.sk,
+          publishableKey: oauthKeys.pk,
+        });
+        const users = createUserService(client);
+        await users.deleteIfExists({ email: fakeUserForOAuth.email });
+      }

95-97: Wait for session-tasks view before resolving the task

After OTP, the actionable UI is the session tasks screen. Waiting on SignUp can race and cause flakiness. Prefer waiting on sessionTask.

Apply this diff:

-      // Resolves task
-      await u.po.signUp.waitForMounted();
+      // Resolves task
+      await u.po.sessionTask.waitForMounted();

If sessionTask.waitForMounted() isn’t available yet, add it to the POM (page-objects/sessionTask.ts) to wait for a stable selector (e.g., .cl-sessionTasks-root).

packages/clerk-js/src/ui/contexts/components/SignUp.ts (1)

118-125: Preserve redirectUrl and prefer taskUrls override in navigateOnSetActive; avoid route-map coupling

navigateOnSetActive drops the redirectUrl and ignores provider-level taskUrls. It also depends on INTERNAL_SESSION_TASK_ROUTE_BY_KEY, which can drift from core URL builders. This diverges from the PR goal to preserve redirectUrl and respect taskUrls.

Apply this diff to unify with the taskUrl computation and preserve redirect/auth params:

-  const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
-    const currentTask = session.currentTask;
-    if (!currentTask) {
-      return navigate(redirectUrl);
-    }
-
-    return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
-  };
+  const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
+    const currentTask = session.currentTask;
+    if (!currentTask) {
+      return navigate(redirectUrl);
+    }
+    const customTaskUrl = clerk.__internal_getOption('taskUrls')?.[currentTask.key];
+    const defaultTaskUrl = buildTaskRedirectUrl(currentTask, {
+      routing: ctx.routing,
+      baseUrl: signUpUrl,
+      authQueryString,
+      path: ctx.path,
+    });
+    const baseTaskUrl = customTaskUrl ?? defaultTaskUrl;
+    const nextUrl = buildURL(
+      {
+        base: baseTaskUrl,
+        hashSearchParams: [{ redirect_url: redirectUrl }, redirectUrls.getPreservedSearchParams()],
+      },
+      { stringify: true },
+    );
+    return navigate(nextUrl);
+  };

Additionally, once the above change is applied, you can drop the INTERNAL_SESSION_TASK_ROUTE_BY_KEY import since the route-map is no longer used.

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

564-578: Add test coverage for preserving redirectUrl

The test correctly verifies navigation to a task URL. However, based on past review comments, you should also add a test that verifies redirectUrl passed to setActive is preserved when navigating to a taskUrl.

#!/bin/bash
# Check if there are tests that verify redirectUrl preservation with taskUrls
rg -A 10 "redirectUrl.*taskUrls?" packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/clerk-js/src/ui/contexts/components/SignIn.ts (4)

40-43: Remove duplicate useRouter() call

You're calling useRouter() twice - once on line 40 and again on line 42. Consolidate to a single call.

-  const { navigate, basePath } = useRouter();
   const { displayConfig, userSettings } = useEnvironment();
-  const { queryParams, queryString } = useRouter();
+  const { navigate, basePath, queryParams, queryString } = useRouter();

123-130: Simplify navigation logic and respect taskUrls configuration

The current implementation uses a hard-coded relative path construction which:

  1. Doesn't respect taskUrls configuration from ClerkProvider
  2. Uses potentially error-prone relative path construction with basePath
   const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
     const currentTask = session.currentTask;
     if (!currentTask) {
       return navigate(redirectUrl);
     }

-    return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
+    const customTaskUrl = clerk.__internal_getOption('taskUrls')?.[currentTask.key];
+    const defaultTaskUrl = buildTaskRedirectUrl(currentTask, {
+      routing: ctx.routing,
+      baseUrl: signInUrl,
+      authQueryString,
+      path: ctx.path,
+    });
+    return navigate(customTaskUrl ?? defaultTaskUrl);
   };

132-140: Use newSession instead of clerk.session for taskUrl computation

The taskUrl is computed using clerk.session which may be outdated when switching to a new pending session. Use the newly resolved session object instead.

This issue was already identified in previous reviews. The taskUrl should be computed from the newSession that's being activated, not from the potentially stale clerk.session.


32-34: Make redirectUrl optional in navigateOnSetActive signature

The redirectUrl parameter should be optional since it may not always be provided to setActive.

-  navigateOnSetActive: (opts: { session: SessionResource; redirectUrl: string }) => Promise<unknown>;
+  navigateOnSetActive: (opts: { session: SessionResource; redirectUrl?: string }) => Promise<void>;
packages/clerk-js/src/core/clerk.ts (4)

1217-1220: Add proper error handling for session ID lookup

The type assertion could cause runtime issues if the session is not found.

 if (typeof session === 'string') {
-  session = (this.client.sessions.find(x => x.id === session) as SignedInSessionResource) || null;
+  const foundSession = this.client.sessions.find(x => x.id === session);
+  if (!foundSession) {
+    throw new Error(`Session with ID "${session}" not found.`);
+  }
+  session = foundSession as SignedInSessionResource;
 }

1232-1234: Pass merged options to avoid false positive warnings

The warning function only receives params which doesn't include taskUrls from this.#options, potentially causing false positive warnings.

-        warnMissingPendingTaskHandlers(params);
+        warnMissingPendingTaskHandlers({ ...this.#options, ...params });

1305-1308: Use newSession instead of this.session for taskUrl

The taskUrl is incorrectly derived from the old session. Use newSession which is already in scope.

 const taskUrl =
   newSession?.status === 'pending' &&
   newSession?.currentTask &&
-  this.#options.taskUrls?.[this.session?.currentTask.key];
+  this.#options.taskUrls?.[newSession.currentTask.key];

1321-1337: Add error handling for navigate callback and fix duplicate navigation

Two issues:

  1. The navigate callback should be wrapped in try-catch to prevent leaving Clerk in an inconsistent state
  2. There's duplicate navigation to redirectUrl at lines 1335 and 1336
 if (taskUrl) {
   const taskUrlWithRedirect = redirectUrl
     ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true })
     : taskUrl;
   await this.navigate(taskUrlWithRedirect);
 } else if (setActiveNavigate && newSession) {
-  await setActiveNavigate({ session: newSession });
+  try {
+    await setActiveNavigate({ session: newSession });
+  } catch (err) {
+    logger.error('setActive.navigate callback failed', err);
+  }
 } else if (redirectUrl) {
   if (this.client.isEligibleForTouch()) {
     const absoluteRedirectUrl = new URL(redirectUrl, window.location.href);
     const redirectUrlWithAuth = this.buildUrlWithAuth(
       this.client.buildTouchUrl({ redirectUrl: absoluteRedirectUrl }),
     );
     await this.navigate(redirectUrlWithAuth);
+    return;
   }
+  await this.navigate(redirectUrl);
 }
-await this.navigate(redirectUrl);
🧹 Nitpick comments (8)
packages/clerk-js/src/ui/contexts/components/SignUp.ts (1)

39-42: Avoid multiple useRouter() calls

You call useRouter() twice to get different properties. Prefer a single call and destructure all needed fields to avoid redundant hook invocations.

Apply this refactor nearby:

-  const { navigate, basePath } = useRouter();
-  const { displayConfig, userSettings } = useEnvironment();
-  const { queryParams, queryString } = useRouter();
+  const { navigate, basePath, queryParams, queryString } = useRouter();
+  const { displayConfig, userSettings } = useEnvironment();
packages/clerk-js/src/core/__tests__/clerk.test.ts (2)

975-991: Inconsistent test name with implementation

The test is named "with pending session" but the describe block starting from line 975 includes general after-auth flow configuration, not just pending sessions.

Consider either:

  1. Renaming the describe block to reflect the broader scope (e.g., "with after-auth flows")
  2. Or moving the forceOrganizationSelection setup to a more appropriate location

1506-1507: Type inconsistency in test data

The mockSession object is defined with status: 'active' on line 1485, but the test data on lines 1506-1507 includes it in signedInSessions array. This seems intentional but could benefit from a comment explaining why an active session appears in the context of testing pending session handling.

Consider adding a comment to clarify the test setup:

   mockClientFetch.mockReturnValue(
     Promise.resolve({
       sessions: [mockSession],
+      // Active session included to test navigation behavior
       signedInSessions: [mockSession],
packages/clerk-js/src/core/sessionTasks.ts (3)

14-14: Consider adding explicit return type

Add an explicit return type for better TypeScript clarity.

-const getTaskEndpoint = (task: SessionTask) => `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[task.key]}`;
+const getTaskEndpoint = (task: SessionTask): string => `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[task.key]}`;

19-33: Add explicit return type

Add an explicit return type for consistency with TypeScript best practices.

 export function buildTaskRedirectUrl(
   task: SessionTask,
   opts: Omit<Parameters<typeof buildRedirectUrl>[0], 'endpoint'>,
-) {
+): string {

38-47: Add explicit return type

Add an explicit return type for consistency.

 export function buildTaskUrl(
   task: SessionTask,
   {
     baseUrl,
   }: {
     baseUrl: string;
   },
-) {
+): string {
packages/clerk-js/src/core/clerk.ts (2)

1554-1574: Consider preserving redirectUrl in buildTasksUrl

The buildTasksUrl method doesn't preserve any redirectUrl that might have been passed to setActive. Consider whether this should be included for consistency with task navigation.

 public buildTasksUrl(): string {
   const currentTask = this.session?.currentTask;
   if (!currentTask) {
     return '';
   }

   const customTaskUrl = this.#options.taskUrls?.[currentTask.key];
   if (customTaskUrl) {
     return customTaskUrl;
   }

   return buildURL(
     {
       base: this.buildSignInUrl(),
       hashPath: `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`,
     },
     {
       stringify: true,
     },
   );
 }

1995-1997: Consider using setActive for pending session navigation

When there's a pending session task, consider using setActive with the navigate callback for consistency instead of direct navigation.

 if (this.session?.currentTask) {
-  return this.navigate(params.taskUrl ?? this.buildTasksUrl());
+  return this.setActive({
+    session: this.session,
+    navigate: async ({ session }) => {
+      await this.navigate(params.taskUrl ?? this.buildTasksUrl());
+    }
+  });
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fe1c8b7 and 2b13e93.

⛔ Files ignored due to path filters (3)
  • packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
  • packages/remix/src/__tests__/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
  • packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (48)
  • .changeset/rich-donuts-agree.md (1 hunks)
  • .changeset/warm-rocks-flow.md (1 hunks)
  • integration/tests/session-tasks-sign-up.test.ts (4 hunks)
  • packages/clerk-js/bundlewatch.config.json (1 hunks)
  • packages/clerk-js/src/core/__tests__/clerk.test.ts (8 hunks)
  • packages/clerk-js/src/core/clerk.ts (23 hunks)
  • packages/clerk-js/src/core/resources/SignIn.ts (1 hunks)
  • packages/clerk-js/src/core/resources/SignUp.ts (1 hunks)
  • packages/clerk-js/src/core/sessionTasks.ts (1 hunks)
  • packages/clerk-js/src/ui/common/redirects.ts (0 hunks)
  • packages/clerk-js/src/ui/common/withRedirect.tsx (3 hunks)
  • packages/clerk-js/src/ui/components/SessionTasks/index.tsx (5 hunks)
  • packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx (3 hunks)
  • packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx (3 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInAccountSwitcher.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativeChannelCodeForm.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOneCodeForm.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorTwo.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoCodeForm.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInSSOCallback.tsx (1 hunks)
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx (6 hunks)
  • packages/clerk-js/src/ui/components/SignIn/__tests__/handleCombinedFlowTransfer.test.ts (5 hunks)
  • packages/clerk-js/src/ui/components/SignIn/handleCombinedFlowTransfer.ts (4 hunks)
  • packages/clerk-js/src/ui/components/SignIn/index.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/shared.ts (2 hunks)
  • packages/clerk-js/src/ui/components/SignUp/SignUpContinue.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignUp/SignUpEmailLinkCard.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignUp/SignUpSSOCallback.tsx (1 hunks)
  • packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx (5 hunks)
  • packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignUp/index.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx (3 hunks)
  • packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx (2 hunks)
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts (4 hunks)
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts (4 hunks)
  • packages/clerk-js/src/ui/lazyModules/components.ts (1 hunks)
  • packages/clerk-js/src/ui/types.ts (2 hunks)
  • packages/clerk-js/src/utils/componentGuards.ts (1 hunks)
  • packages/nextjs/src/client-boundary/controlComponents.ts (1 hunks)
  • packages/nextjs/src/index.ts (0 hunks)
  • packages/react/src/components/controlComponents.tsx (2 hunks)
  • packages/react/src/components/index.ts (1 hunks)
  • packages/react/src/isomorphicClerk.ts (2 hunks)
  • packages/types/src/clerk.ts (6 hunks)
  • packages/vue/src/components/controlComponents.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/nextjs/src/index.ts
  • packages/clerk-js/src/ui/common/redirects.ts
🚧 Files skipped from review as they are similar to previous changes (36)
  • packages/react/src/components/index.ts
  • packages/react/src/components/controlComponents.tsx
  • packages/clerk-js/src/ui/components/SignUp/SignUpSSOCallback.tsx
  • packages/clerk-js/src/ui/common/withRedirect.tsx
  • .changeset/rich-donuts-agree.md
  • packages/clerk-js/src/ui/components/SignIn/handleCombinedFlowTransfer.ts
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInSSOCallback.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorTwo.tsx
  • packages/clerk-js/src/ui/lazyModules/components.ts
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoCodeForm.tsx
  • packages/clerk-js/src/ui/types.ts
  • packages/vue/src/components/controlComponents.ts
  • .changeset/warm-rocks-flow.md
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativeChannelCodeForm.tsx
  • packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx
  • packages/clerk-js/src/ui/components/SignUp/SignUpContinue.tsx
  • packages/clerk-js/src/ui/components/SignIn/shared.ts
  • packages/clerk-js/src/ui/components/SignIn/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInAccountSwitcher.tsx
  • packages/clerk-js/src/ui/components/SessionTasks/index.tsx
  • packages/clerk-js/src/utils/componentGuards.ts
  • packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx
  • packages/clerk-js/src/core/resources/SignUp.ts
  • packages/clerk-js/bundlewatch.config.json
  • packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx
  • packages/clerk-js/src/ui/components/SignUp/SignUpEmailLinkCard.tsx
  • packages/clerk-js/src/ui/components/SignIn/tests/handleCombinedFlowTransfer.test.ts
  • packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInFactorOneCodeForm.tsx
  • packages/types/src/clerk.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx
  • packages/react/src/isomorphicClerk.ts
🧰 Additional context used
📓 Path-based instructions (16)
packages/clerk-js/src/ui/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)

packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure

Files:

  • packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
**/*.{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/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • integration/tests/session-tasks-sign-up.test.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.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/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • integration/tests/session-tasks-sign-up.test.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.ts
packages/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.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/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.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/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • integration/tests/session-tasks-sign-up.test.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.ts
**/*.{jsx,tsx}

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

**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components

**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components: UserProfile, NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...

Files:

  • packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
**/*.{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/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • integration/tests/session-tasks-sign-up.test.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.ts
**/*.tsx

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

**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering

Files:

  • packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
**/*

⚙️ 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/ui/components/UserButton/useMultisessionActions.tsx
  • packages/clerk-js/src/core/resources/SignIn.ts
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • integration/tests/session-tasks-sign-up.test.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/ui/components/SignUp/index.tsx
  • packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/core/clerk.ts
integration/**

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

Framework integration templates and E2E tests should be placed under the integration/ directory

Files:

  • integration/tests/session-tasks-sign-up.test.ts
integration/**/*

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

End-to-end tests and integration templates must be located in the 'integration/' directory.

Files:

  • integration/tests/session-tasks-sign-up.test.ts
integration/**/*.{test,spec}.{js,ts}

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

Integration tests should use Playwright.

Files:

  • integration/tests/session-tasks-sign-up.test.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/core/__tests__/clerk.test.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/core/__tests__/clerk.test.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/core/__tests__/clerk.test.ts
🧠 Learnings (1)
📚 Learning: 2025-08-08T19:00:08.931Z
Learnt from: LauraBeatris
PR: clerk/javascript#6486
File: packages/clerk-js/src/core/clerk.ts:1303-1344
Timestamp: 2025-08-08T19:00:08.931Z
Learning: In Clerk's navigation system for pending sessions, `taskUrls` (configured at the ClerkProvider level) should take priority over the `navigate` callback (from All-In-One components). This ensures that explicit provider-level configuration overrides component-level navigation logic, providing developers with predictable control over navigation behavior.

Applied to files:

  • packages/clerk-js/src/core/clerk.ts
🧬 Code Graph Analysis (8)
packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx (3)
packages/clerk-js/src/ui/elements/contexts/index.tsx (1)
  • useCardState (42-70)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/clerk-js/src/core/sessionTasks.ts (1)
  • buildTaskUrl (38-47)
packages/clerk-js/src/ui/contexts/components/SignUp.ts (5)
packages/types/src/session.ts (1)
  • SessionResource (208-261)
packages/clerk-js/src/ui/router/RouteContext.tsx (1)
  • useRouter (33-39)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/clerk-js/src/core/sessionTasks.ts (2)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskRedirectUrl (19-33)
integration/tests/session-tasks-sign-up.test.ts (3)
integration/presets/index.ts (1)
  • appConfigs (14-30)
integration/presets/envs.ts (1)
  • instanceKeys (23-23)
integration/testUtils/usersService.ts (1)
  • createUserService (101-218)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (6)
packages/types/src/session.ts (1)
  • SessionResource (208-261)
packages/clerk-js/src/ui/types.ts (1)
  • SignInCtx (64-68)
packages/clerk-js/src/ui/router/RouteContext.tsx (1)
  • useRouter (33-39)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/clerk-js/src/core/sessionTasks.ts (2)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskRedirectUrl (19-33)
packages/clerk-js/src/core/__tests__/clerk.test.ts (2)
packages/types/src/clerk.ts (1)
  • Clerk (164-886)
packages/types/src/session.ts (2)
  • PendingSessionResource (275-279)
  • ActiveSessionResource (267-270)
packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx (2)
packages/clerk-js/src/ui/common/withRedirect.tsx (2)
  • withRedirectToSignInTask (93-113)
  • withRedirectToAfterSignIn (55-72)
packages/clerk-js/src/ui/elements/contexts/index.tsx (1)
  • withCardStateProvider (72-81)
packages/clerk-js/src/core/sessionTasks.ts (4)
packages/types/src/session.ts (1)
  • SessionTask (332-337)
packages/clerk-js/src/ui/common/redirects.ts (1)
  • buildRedirectUrl (58-79)
packages/clerk-js/src/utils/url.ts (1)
  • buildURL (81-155)
packages/types/src/clerk.ts (1)
  • ClerkOptions (967-1087)
packages/clerk-js/src/core/clerk.ts (7)
packages/clerk-js/src/utils/componentGuards.ts (1)
  • isSignedInAndSingleSessionModeEnabled (9-11)
packages/types/src/clerk.ts (1)
  • SetActiveParams (1189-1235)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/types/src/session.ts (2)
  • SignedInSessionResource (285-285)
  • SessionResource (208-261)
packages/clerk-js/src/core/sessionTasks.ts (3)
  • warnMissingPendingTaskHandlers (49-63)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskUrl (38-47)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/clerk-js/src/utils/runtime.ts (1)
  • inBrowser (1-3)
⏰ 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-cloud-platform/scan
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (22)
packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx (1)

4-5: Importing sessionTasks and environment looks correct

Using buildTaskUrl and useEnvironment aligns this hook with the new explicit after-auth navigation model.

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

252-256: Redirect URL decoration centralized and consistent — looks good

Using SignIn.clerk.buildUrlWithAuth(params.redirectUrl) once and passing it through to both create and prepareFirstFactor removes duplication and preserves dev-browser auth context across OAuth/SSO flows.

Also applies to: 261-263, 269-271

packages/clerk-js/src/ui/contexts/components/SignUp.ts (1)

127-135: taskUrl computation aligns with the new model

Preferring taskUrls override and falling back to buildTaskRedirectUrl is the right approach and consistent with preserving auth/query context.

packages/clerk-js/src/ui/components/SignUp/index.tsx (2)

47-57: Propagating taskUrl to SSO callback is correct

Passing taskUrl from context into SignUpSSOCallback keeps SSO flows consistent with the new per-task routing model.


5-6: Alias '@/…' is correctly configured in packages/clerk-js/tsconfig.json

The import import { usePreloadTasks } from '@/ui/hooks/usePreloadTasks'; will resolve to packages/clerk-js/src/ui/hooks/usePreloadTasks because:

  • packages/clerk-js/tsconfig.json (lines 23–25) defines
    "paths": {
      "@/*": ["./src/*"]
    }

No changes needed.

packages/clerk-js/src/core/__tests__/clerk.test.ts (6)

447-457: LGTM!

The test correctly validates that setActive calls both session.touch and the navigate callback for active sessions. This aligns with the new navigate option for after-auth flows.


528-536: LGTM!

The test correctly validates the basic behavior of setActive with a pending session - it should call session.touch as expected.


580-590: LGTM!

The test correctly validates that the navigate callback is invoked when provided to setActive for a pending session.


992-1038: LGTM!

The test correctly validates navigation to a task URL when a pending session is returned from sign-up completion. The use of /sign-in#/tasks/choose-organization for the expected navigation path correctly reflects the new per-task routing model.


1546-1547: Consistent test data structure

Good job updating test data to include sessions in both sessions and signedInSessions arrays consistently across tests. This properly reflects the expected client state.

Also applies to: 1567-1568


538-549: Add explicit verification that hooks are not called

The test should explicitly verify that the hooks were not called rather than relying on the lack of assertion failure.

   const onBeforeSetActive = jest.fn();
   (window as any).__unstable__onBeforeSetActive = onBeforeSetActive;

   const sut = new Clerk(productionPublishableKey);
   await sut.load();
   await sut.setActive({ session: mockSession as any as ActiveSessionResource });
   expect(onBeforeSetActive).not.toHaveBeenCalled();
+  expect(mockSession.touch).toHaveBeenCalled();

Likely an incorrect or invalid review comment.

packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx (4)

239-241: LGTM! Proper use of navigate callback

Good implementation of the new navigate callback pattern for setActive. This correctly passes the session context to enable task-based navigation.


392-394: Consistent implementation of navigate callback

The navigation callback pattern is consistently applied here as well. Good job maintaining consistency across different sign-in flows.


476-477: LGTM! Navigate callback properly passed to combined flow

The navigateOnSetActive is correctly passed through to the combined flow handler, ensuring consistent navigation behavior.


684-686: Good HOC composition for task navigation

The addition of withRedirectToSignInTask as the outermost HOC is appropriate, as it should check for pending tasks before other redirects. The composition order is correct.

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

1-6: LGTM! Clean imports and dependencies

The imports are well-organized and appropriate for the session task handling functionality.


49-63: LGTM! Well-implemented warning logic

The warning function properly checks for the presence of task handlers and provides a helpful warning message when they're missing. The use of logger.warnOnce ensures the warning doesn't spam the console.

packages/clerk-js/src/core/clerk.ts (5)

1201-1204: LGTM! Clean parameter destructuring

The new SetActiveParams signature properly supports the navigate callback and other parameters through a single params object.


1666-1671: LGTM! Clean implementation of redirectToTasks

The new redirectToTasks method properly integrates with the task navigation system.


1854-1869: Good implementation of setActiveNavigate helper

The helper function properly handles both task navigation and regular redirects. The logic is clear and follows the expected navigation precedence.


1871-1877: LGTM! Proper use of navigate callback in OAuth flow

The OAuth completion flow correctly uses the new navigate callback pattern with setActiveNavigate.


2167-2178: LGTM! Consistent implementation in Web3 flow

The Web3 authentication flow properly implements the same navigation pattern as OAuth.

Comment on lines +551 to +562
it('does not call __unstable__onAfterSetActive after session.touch', async () => {
mockSession.touch.mockReturnValueOnce(Promise.resolve());
mockClientFetch.mockReturnValue(Promise.resolve({ signedInSessions: [mockSession] }));

const onAfterSetActive = jest.fn();
(window as any).__unstable__onAfterSetActive = onAfterSetActive;

const sut = new Clerk(productionPublishableKey);
await sut.load();
await sut.setActive({ session: mockSession as any as ActiveSessionResource });
expect(onAfterSetActive).not.toHaveBeenCalled();
});
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

Add explicit verification that hooks are not called

Similar to the previous comment, the test should explicitly verify that the hooks were not called.

   const onAfterSetActive = jest.fn();
   (window as any).__unstable__onAfterSetActive = onAfterSetActive;

   const sut = new Clerk(productionPublishableKey);
   await sut.load();
   await sut.setActive({ session: mockSession as any as ActiveSessionResource });
   expect(onAfterSetActive).not.toHaveBeenCalled();
+  expect(mockSession.touch).toHaveBeenCalled();
📝 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
it('does not call __unstable__onAfterSetActive after session.touch', async () => {
mockSession.touch.mockReturnValueOnce(Promise.resolve());
mockClientFetch.mockReturnValue(Promise.resolve({ signedInSessions: [mockSession] }));
const onAfterSetActive = jest.fn();
(window as any).__unstable__onAfterSetActive = onAfterSetActive;
const sut = new Clerk(productionPublishableKey);
await sut.load();
await sut.setActive({ session: mockSession as any as ActiveSessionResource });
expect(onAfterSetActive).not.toHaveBeenCalled();
});
it('does not call __unstable__onAfterSetActive after session.touch', async () => {
mockSession.touch.mockReturnValueOnce(Promise.resolve());
mockClientFetch.mockReturnValue(Promise.resolve({ signedInSessions: [mockSession] }));
const onAfterSetActive = jest.fn();
(window as any).__unstable__onAfterSetActive = onAfterSetActive;
const sut = new Clerk(productionPublishableKey);
await sut.load();
await sut.setActive({ session: mockSession as any as ActiveSessionResource });
expect(onAfterSetActive).not.toHaveBeenCalled();
expect(mockSession.touch).toHaveBeenCalled();
});
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/__tests__/clerk.test.ts around lines 551 to 562,
the test only sets and asserts a single hook; add explicit verification for the
related hook(s) so the test clearly asserts no lifecycle hooks are invoked.
Create spies for both window.__unstable__onBeforeSetActive and
window.__unstable__onAfterSetActive (or any other related hooks used in similar
tests), assign them to window, run the same actions (load and setActive), and
add expect(...).not.toHaveBeenCalled() assertions for each spy; restore/cleanup
the window properties after the test.

Comment on lines +24 to 29
const { setActive, signOut, openUserProfile } = useClerk();
const card = useCardState();
const { signedInSessions, otherSessions } = useMultipleSessions({ user: opts.user });
const { navigate } = useRouter();
const { displayConfig } = useEnvironment();

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Prefer accessing the Clerk instance to support taskUrls override resolution

You destructure methods from useClerk(), but you’ll need the Clerk instance to honor a provider-level taskUrls override inside your navigate callback.

Apply this diff to keep access to the Clerk object while preserving current usage:

-  const { setActive, signOut, openUserProfile } = useClerk();
+  const clerk = useClerk();
+  const { setActive, signOut, openUserProfile } = clerk;
📝 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 { setActive, signOut, openUserProfile } = useClerk();
const card = useCardState();
const { signedInSessions, otherSessions } = useMultipleSessions({ user: opts.user });
const { navigate } = useRouter();
const { displayConfig } = useEnvironment();
const clerk = useClerk();
const { setActive, signOut, openUserProfile } = clerk;
const card = useCardState();
const { signedInSessions, otherSessions } = useMultipleSessions({ user: opts.user });
const { navigate } = useRouter();
const { displayConfig } = useEnvironment();

Comment on lines +76 to +95
return setActive({
session,
navigate: async ({ session }) => {
if (!session.currentTask && opts.afterSwitchSessionUrl) {
await navigate(opts.afterSwitchSessionUrl);
return;
}

if (session.currentTask) {
await navigate(
buildTaskUrl(session.currentTask, {
baseUrl: opts.signInUrl ?? displayConfig.signInUrl,
}),
);
}
},
}).finally(() => {
card.setIdle();
opts.actionCompleteCallback?.();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Honor taskUrls overrides when navigating to session tasks during session switch

When a currentTask exists, the navigate callback always uses buildTaskUrl. If a developer provided taskUrls via ClerkProvider, we should prefer that mapping for consistency with the rest of the UI contexts.

Apply this diff to prefer the override and fallback to the default URL:

-    return setActive({
+    return setActive({
       session,
       navigate: async ({ session }) => {
         if (!session.currentTask && opts.afterSwitchSessionUrl) {
           await navigate(opts.afterSwitchSessionUrl);
           return;
         }
 
-        if (session.currentTask) {
-          await navigate(
-            buildTaskUrl(session.currentTask, {
-              baseUrl: opts.signInUrl ?? displayConfig.signInUrl,
-            }),
-          );
-        }
+        if (session.currentTask) {
+          const override = clerk.__internal_getOption('taskUrls')?.[session.currentTask.key];
+          const target =
+            override ??
+            buildTaskUrl(session.currentTask, {
+              baseUrl: opts.signInUrl ?? displayConfig.signInUrl,
+            });
+          await navigate(target);
+        }
       },
     }).finally(() => {
       card.setIdle();
       opts.actionCompleteCallback?.();
     });
📝 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
return setActive({
session,
navigate: async ({ session }) => {
if (!session.currentTask && opts.afterSwitchSessionUrl) {
await navigate(opts.afterSwitchSessionUrl);
return;
}
if (session.currentTask) {
await navigate(
buildTaskUrl(session.currentTask, {
baseUrl: opts.signInUrl ?? displayConfig.signInUrl,
}),
);
}
},
}).finally(() => {
card.setIdle();
opts.actionCompleteCallback?.();
});
return setActive({
session,
navigate: async ({ session }) => {
if (!session.currentTask && opts.afterSwitchSessionUrl) {
await navigate(opts.afterSwitchSessionUrl);
return;
}
if (session.currentTask) {
const override = clerk.__internal_getOption('taskUrls')?.[session.currentTask.key];
const target =
override ??
buildTaskUrl(session.currentTask, {
baseUrl: opts.signInUrl ?? displayConfig.signInUrl,
});
await navigate(target);
}
},
}).finally(() => {
card.setIdle();
opts.actionCompleteCallback?.();
});
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
around lines 76 to 95 the navigate branch that handles session.currentTask
always calls buildTaskUrl; update it to first prefer any provided taskUrls
override (opts.taskUrls ?? displayConfig.taskUrls) and, if that mapping contains
an entry for session.currentTask, navigate to that URL; otherwise fall back to
the existing buildTaskUrl call (still using baseUrl: opts.signInUrl ??
displayConfig.signInUrl). Ensure the lookup supports string mappings (and falls
back to default behavior when no mapping or entry exists), then keep the same
finally block that calls card.setIdle() and opts.actionCompleteCallback?.().

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

♻️ Duplicate comments (13)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (4)

33-34: Make redirectUrl optional and refine return type

redirectUrl can be absent on setActive. Also, the callback returns nothing meaningful; use Promise.

-  navigateOnSetActive: (opts: { session: SessionResource; redirectUrl: string }) => Promise<unknown>;
+  navigateOnSetActive: (opts: { session: SessionResource; redirectUrl?: string }) => Promise<void>;

41-43: Consolidate duplicate useRouter() calls

You're calling useRouter() twice on lines 41 and 43, which is inefficient.

-  const { navigate, basePath } = useRouter();
+  const { navigate, basePath, queryParams, queryString } = useRouter();
   const { displayConfig, userSettings } = useEnvironment();
-  const { queryParams, queryString } = useRouter();

124-131: Honor taskUrls configuration and simplify navigation logic

The current implementation manually constructs the task URL path and doesn't respect ClerkProvider's taskUrls configuration.

   const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
     const currentTask = session.currentTask;
     if (!currentTask) {
       return navigate(redirectUrl);
     }
 
-    return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
+    const customTaskUrl = clerk.__internal_getOption('taskUrls')?.[currentTask.key];
+    const target = customTaskUrl ?? buildTaskUrl(currentTask, { baseUrl: signInUrl });
+    return navigate(target);
   };

133-141: Use newSession for computing taskUrl to avoid stale state

clerk.session might not be updated yet when switching to a new pending session, causing taskUrl to be null. Use the resolved session from setActive instead.

Since taskUrl is computed during context initialization and not during the setActive flow, consider computing it dynamically or documenting this limitation.

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

7-12: Consider using satisfies operator for better type safety

Using satisfies ensures the object matches the expected type while preserving the literal types.

-export const INTERNAL_SESSION_TASK_ROUTE_BY_KEY: Record<SessionTask['key'], string> = {
-  'choose-organization': 'choose-organization',
-} as const;
+export const INTERNAL_SESSION_TASK_ROUTE_BY_KEY = {
+  'choose-organization': 'choose-organization',
+} as const satisfies Record<SessionTask['key'], string>;

38-47: Add explicit return type annotation and consider supporting redirectUrl preservation

The function should have an explicit return type. Also, consider supporting an optional redirectUrl parameter that can be preserved in the task URL for maintaining navigation context.

 export function buildTaskUrl(
   task: SessionTask,
   {
     baseUrl,
+    redirectUrl,
   }: {
     baseUrl: string;
+    redirectUrl?: string;
   },
-) {
-  return buildURL({ base: baseUrl, hashPath: getTaskEndpoint(task) }, { stringify: true });
+): string {
+  const params = new URLSearchParams();
+  if (redirectUrl) {
+    params.set('redirect_url', redirectUrl);
+  }
+  return buildURL(
+    { 
+      base: baseUrl, 
+      hashPath: getTaskEndpoint(task),
+      hashSearchParams: params
+    }, 
+    { stringify: true }
+  );
 }
packages/clerk-js/src/core/clerk.ts (7)

1232-1234: Fix warning function call to include merged options

Based on the past review comments, the warning function receives incomplete data. params doesn't contain taskUrls from the global options, causing false positive warnings.

Apply this fix to pass the complete configuration:

-warnMissingPendingTaskHandlers({ ...this.#options, ...params });
+warnMissingPendingTaskHandlers({ ...this.#options, ...params });

Wait, let me check the current code again. The current implementation already merges both objects, so this appears to be correctly implemented now.


1321-1325: Improve redirectUrl preservation for different URL types

The current implementation always appends redirectUrl as hash parameters, which could introduce unexpected hash fragments for non-hash URLs.

 if (taskUrl) {
-  const taskUrlWithRedirect = redirectUrl
-    ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true })
-    : taskUrl;
+  let taskUrlWithRedirect = taskUrl;
+  if (redirectUrl) {
+    const useHash = taskUrl.includes('#');
+    const buildOpts: any = { base: taskUrl };
+    if (useHash) {
+      buildOpts.hashSearchParams = { redirectUrl };
+    } else {
+      buildOpts.searchParams = { redirectUrl };
+    }
+    taskUrlWithRedirect = buildURL(buildOpts, { stringify: true }) as string;
+  }
   await this.navigate(taskUrlWithRedirect);

1326-1327: Add error handling for consumer navigate callback

The consumer-supplied navigate callback could throw exceptions, leaving Clerk in an inconsistent state with __internal_setActiveInProgress stuck as true.

 if (setActiveNavigate && newSession) {
-  await setActiveNavigate({ session: newSession });
+  try {
+    await setActiveNavigate({ session: newSession });
+  } catch (err) {
+    logger.error('setActive.navigate callback failed', err);
+    // Re-throw to maintain existing behavior but ensure cleanup happens
+    throw err;
+  }
   return;
 }

1329-1336: Fix duplicate redirectUrl navigation

There appears to be duplicate navigation logic where redirectUrl is navigated to twice - once conditionally inside the if block and once unconditionally after.

 } else if (redirectUrl) {
   if (this.client.isEligibleForTouch()) {
     const absoluteRedirectUrl = new URL(redirectUrl, window.location.href);
     const redirectUrlWithAuth = this.buildUrlWithAuth(
       this.client.buildTouchUrl({ redirectUrl: absoluteRedirectUrl }),
     );
     await this.navigate(redirectUrlWithAuth);
+    return;
   }
-}
-await this.navigate(redirectUrl);
+  await this.navigate(redirectUrl);
+}

1854-1869: setActiveNavigate helper needs redirectUrl propagation

The helper function doesn't receive or use the redirectUrl parameter, which could cause the redirect URL to be lost during task navigation in OAuth callbacks.

 const setActiveNavigate = async ({
   session,
   baseUrl,
   redirectUrl,
 }: {
   session: SessionResource;
   baseUrl: string;
   redirectUrl: string;
 }) => {
   if (!session.currentTask) {
     await this.navigate(redirectUrl);
     return;
   }

-  await navigate(params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl }));
+  const taskUrl = params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl });
+  const taskUrlWithRedirect = redirectUrl
+    ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true })
+    : taskUrl;
+  await navigate(taskUrlWithRedirect);
 };

2167-2178: Apply consistent redirectUrl handling in Web3 flows

The Web3 setActiveNavigate helper also needs to handle redirectUrl propagation to maintain consistency across all authentication flows.

 const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
   if (!session.currentTask) {
     await this.navigate(redirectUrl);
     return;
   }

-  await this.navigate(
-    buildTaskUrl(session.currentTask, {
-      baseUrl: displayConfig.signInUrl,
-    }),
-  );
+  const taskUrl = buildTaskUrl(session.currentTask, {
+    baseUrl: displayConfig.signInUrl,
+  });
+  const taskUrlWithRedirect = redirectUrl
+    ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true })
+    : taskUrl;
+  await this.navigate(taskUrlWithRedirect);
 };

1217-1220: Add proper error handling for session ID lookup

The current implementation unsafely casts the result of find() which could be undefined. This could lead to runtime errors if a session ID doesn't exist.

 if (typeof session === 'string') {
-  session = (this.client.sessions.find(x => x.id === session) as SignedInSessionResource) || null;
+  const foundSession = this.client.sessions.find(x => x.id === session);
+  if (!foundSession) {
+    throw new Error(`Session with ID "${session}" not found in client sessions.`);
+  }
+  session = foundSession as SignedInSessionResource;
 }
🧹 Nitpick comments (7)
packages/clerk-js/src/utils/redirects.ts (4)

13-34: Add explicit return type annotations for helper functions

While the main buildRedirectUrl function has a return type annotation, the helper functions lack them.

-const buildHashBasedUrl = (authQueryString: AuthQueryString, endpoint: string): string => {
+const buildHashBasedUrl = (authQueryString: AuthQueryString, endpoint: string): string => {
   // Strip hash to get the URL where we're mounted
   const hash = endpoint + (authQueryString ? `?${authQueryString}` : '');
   return buildURL({ hash }, { stringify: true });
 };

-const buildPathBasedUrl = (path: string, authQueryString: AuthQueryString, endpoint: string): string => {
+const buildPathBasedUrl = (path: string, authQueryString: AuthQueryString, endpoint: string): string => {
   const searchArg = authQueryString ? { search: '?' + authQueryString } : {};
   return buildURL(
     {
       pathname: path + endpoint,
       ...searchArg,
     },
     { stringify: true },
   );
 };

-const buildVirtualBasedUrl = (base: string, authQueryString: AuthQueryString, endpoint: string): string => {
+const buildVirtualBasedUrl = (base: string, authQueryString: AuthQueryString, endpoint: string): string => {
   const hash = endpoint + (authQueryString ? `?${authQueryString}` : '');
   return buildURL({ base, hash }, { stringify: true });
 };

36-40: Type assertion is unnecessary with TypeScript's control flow analysis

The buildURL function already returns string when stringify: true is passed, so the type assertion on Line 39 is redundant.

 const buildHashBasedUrl = (authQueryString: AuthQueryString, endpoint: string): string => {
   // Strip hash to get the URL where we're mounted
   const hash = endpoint + (authQueryString ? `?${authQueryString}` : '');
-  return buildURL({ hash }, { stringify: true });
+  return buildURL({ hash }, { stringify: true });
 };

42-51: Type assertion is unnecessary

Same as above - the type assertion is not needed when stringify: true is passed.

 const buildPathBasedUrl = (path: string, authQueryString: AuthQueryString, endpoint: string): string => {
   const searchArg = authQueryString ? { search: '?' + authQueryString } : {};
   return buildURL(
     {
       pathname: path + endpoint,
       ...searchArg,
     },
     { stringify: true },
-  );
+  );
 };

53-56: Type assertion is unnecessary

Same as above - the type assertion is not needed.

 const buildVirtualBasedUrl = (base: string, authQueryString: AuthQueryString, endpoint: string): string => {
   const hash = endpoint + (authQueryString ? `?${authQueryString}` : '');
-  return buildURL({ base, hash }, { stringify: true });
+  return buildURL({ base, hash }, { stringify: true });
 };
packages/clerk-js/src/core/sessionTasks.ts (3)

14-14: Add explicit return type annotation

According to the coding guidelines, functions should have explicit return types.

-const getTaskEndpoint = (task: SessionTask) => `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[task.key]}`;
+const getTaskEndpoint = (task: SessionTask): string => `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[task.key]}`;

19-33: Add explicit return type annotation

According to the coding guidelines, functions should have explicit return types.

 export function buildTaskRedirectUrl(
   task: SessionTask,
   opts: Omit<Parameters<typeof buildRedirectUrl>[0], 'endpoint'>,
-) {
+): string {

49-63: Add explicit return type annotation

According to the coding guidelines, functions should have explicit return types.

-export function warnMissingPendingTaskHandlers(options: Record<string, unknown>) {
+export function warnMissingPendingTaskHandlers(options: Record<string, unknown>): void {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2b13e93 and 9adcc3c.

📒 Files selected for processing (7)
  • packages/clerk-js/src/core/clerk.ts (23 hunks)
  • packages/clerk-js/src/core/sessionTasks.ts (1 hunks)
  • packages/clerk-js/src/ui/common/redirects.ts (1 hunks)
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts (4 hunks)
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts (4 hunks)
  • packages/clerk-js/src/utils/index.ts (2 hunks)
  • packages/clerk-js/src/utils/redirects.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
🧰 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/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/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/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/utils/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/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/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/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/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/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/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/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/redirects.ts
  • packages/clerk-js/src/utils/index.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/sessionTasks.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/**/index.{js,ts}

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

Use tree-shaking friendly exports

Files:

  • packages/clerk-js/src/utils/index.ts
**/index.ts

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

Use index.ts files for clean imports but avoid deep barrel exports

Avoid barrel files (index.ts re-exports) as they can cause circular dependencies

Files:

  • packages/clerk-js/src/utils/index.ts
packages/clerk-js/src/ui/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)

packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure

Files:

  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
🧠 Learnings (1)
📚 Learning: 2025-08-08T19:00:08.931Z
Learnt from: LauraBeatris
PR: clerk/javascript#6486
File: packages/clerk-js/src/core/clerk.ts:1303-1344
Timestamp: 2025-08-08T19:00:08.931Z
Learning: In Clerk's navigation system for pending sessions, `taskUrls` (configured at the ClerkProvider level) should take priority over the `navigate` callback (from All-In-One components). This ensures that explicit provider-level configuration overrides component-level navigation logic, providing developers with predictable control over navigation behavior.

Applied to files:

  • packages/clerk-js/src/core/clerk.ts
🧬 Code Graph Analysis (4)
packages/clerk-js/src/utils/redirects.ts (1)
packages/clerk-js/src/utils/url.ts (1)
  • buildURL (81-155)
packages/clerk-js/src/core/clerk.ts (8)
packages/clerk-js/src/utils/componentGuards.ts (1)
  • isSignedInAndSingleSessionModeEnabled (9-11)
packages/types/src/clerk.ts (1)
  • SetActiveParams (1189-1235)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/types/src/session.ts (2)
  • SignedInSessionResource (285-285)
  • SessionResource (208-261)
packages/clerk-js/src/core/sessionTasks.ts (3)
  • warnMissingPendingTaskHandlers (49-63)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskUrl (38-47)
packages/clerk-js/src/utils/url.ts (1)
  • buildURL (81-155)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/clerk-js/src/utils/runtime.ts (1)
  • inBrowser (1-3)
packages/clerk-js/src/core/sessionTasks.ts (4)
packages/types/src/session.ts (1)
  • SessionTask (332-337)
packages/clerk-js/src/utils/redirects.ts (1)
  • buildRedirectUrl (13-34)
packages/clerk-js/src/utils/url.ts (1)
  • buildURL (81-155)
packages/types/src/clerk.ts (2)
  • SetActiveParams (1189-1235)
  • ClerkOptions (967-1087)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (5)
packages/types/src/session.ts (1)
  • SessionResource (208-261)
packages/clerk-js/src/ui/types.ts (1)
  • SignInCtx (64-68)
packages/clerk-js/src/ui/router/RouteContext.tsx (1)
  • useRouter (33-39)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/clerk-js/src/core/sessionTasks.ts (2)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskRedirectUrl (19-33)
⏰ 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 (10)
packages/clerk-js/src/utils/index.ts (1)

1-29: LGTM!

The export order change and addition of the redirects module export are appropriate.

packages/clerk-js/src/ui/common/redirects.ts (1)

1-47: LGTM!

The refactoring to use the centralized buildRedirectUrl utility simplifies the code and promotes consistency across the codebase.

packages/clerk-js/src/core/clerk.ts (8)

1201-1204: LGTM: Clean parameter destructuring and method signature migration

The refactored method signature properly adopts the new SetActiveParams interface with clear parameter destructuring. The renaming of navigate to setActiveNavigate provides good internal clarity.


1305-1308: Good fix: taskUrl now correctly uses newSession

The implementation correctly derives taskUrl from newSession instead of the potentially stale this.session. This resolves the previous issue where task URLs wouldn't be computed correctly during session transitions.


1321-1327: Verify precedence order aligns with requirements

Based on the retrieved learning, taskUrls (provider-level configuration) should take priority over the navigate callback (AIO component level). The current order correctly implements this precedence:

  1. taskUrl (from provider-level taskUrls)
  2. setActiveNavigate (from AIO components)
  3. redirectUrl (fallback)

This matches the expected behavior where explicit provider configuration overrides component-level logic.


1554-1574: Excellent addition: buildTasksUrl() method

This new public method provides a clean way to build task URLs with proper fallback logic:

  1. Check for custom taskUrls configuration first
  2. Fall back to default sign-in URL with task-specific hash path

The implementation correctly handles the case where no current task exists.


1666-1671: Good companion method: redirectToTasks()

This method pairs well with buildTasksUrl() to provide a complete public API for task navigation. The implementation follows the same pattern as other redirect methods.


554-554: Good refactoring: Consistent guard usage

The replacement of inline component guards with isSignedInAndSingleSessionModeEnabled provides better consistency and maintainability across openSignIn and openSignUp methods.

Also applies to: 688-688


1354-1356: Essential: Proper cleanup in finally block

The addition of a finally block ensures that __internal_setActiveInProgress is always reset to false, preventing the library from getting stuck in an inconsistent state even if errors occur.


154-154: Clean imports: Task-related utilities

The import statement properly brings in the new session task utilities (buildTaskUrl, INTERNAL_SESSION_TASK_ROUTE_BY_KEY, warnMissingPendingTaskHandlers) that support the enhanced task navigation functionality.

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: 3

🔭 Outside diff range comments (2)
packages/nextjs/src/client-boundary/controlComponents.ts (1)

3-18: Add a deprecated alias and enforce a minimum @clerk/clerk-react version

We verified there are no lingering RedirectToTask imports in the monorepo. To smooth the rename from RedirectToTaskRedirectToTasks for Next.js consumers, please:

• In packages/nextjs/src/client-boundary/controlComponents.ts, append:

// Temporary BC alias for vX.Y → vX+1; remove in next major
export { RedirectToTasks as RedirectToTask } from '@clerk/clerk-react';

• In packages/nextjs/package.json, add a peerDependencies entry requiring the release of @clerk/clerk-react that ships RedirectToTasks (update ^X.Y.Z accordingly):

"peerDependencies": {
  "@clerk/clerk-react": "^X.Y.Z"
}

• No further imports of RedirectToTask exist—internal usages are fully migrated.

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

1305-1339: Navigation logic is well-structured but needs minor improvements.

The current implementation correctly prioritizes taskUrl over setActiveNavigate (as per learnings), but has some issues:

  1. Redirect URL preservation: Current logic always uses hashSearchParams, which may not be appropriate for non-hash URLs
  2. Error handling: Missing try-catch for the navigate callback
           if (taskUrl) {
-            const taskUrlWithRedirect = redirectUrl
-              ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true })
-              : taskUrl;
+            let taskUrlWithRedirect = taskUrl;
+            if (redirectUrl) {
+              const useHash = taskUrl.includes('#');
+              const buildOpts: any = { base: taskUrl };
+              if (useHash) {
+                buildOpts.hashSearchParams = { redirectUrl };
+              } else {
+                buildOpts.searchParams = { redirectUrl };
+              }
+              taskUrlWithRedirect = buildURL(buildOpts, { stringify: true }) as string;
+            }
             await this.navigate(taskUrlWithRedirect);
+            return;
           } else if (setActiveNavigate && newSession) {
-            await setActiveNavigate({ session: newSession });
+            try {
+              await setActiveNavigate({ session: newSession });
+            } catch (err) {
+              logger.error('setActive.navigate callback failed', err);
+            }
+            return;
♻️ Duplicate comments (3)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (1)

32-33: Fix function signature: make redirectUrl optional and improve return type.

Based on the context and learning that redirectUrl can be absent in setActive, the parameter should be optional. Also, the callback doesn't return meaningful data.

-  navigateOnSetActive: (opts: { session: SessionResource; redirectUrl: string }) => Promise<unknown>;
+  navigateOnSetActive: (opts: { session: SessionResource; redirectUrl?: string }) => Promise<void>;
packages/clerk-js/src/core/clerk.ts (2)

1232-1234: Fix argument to warning function.

The warning function receives only params but needs access to taskUrls from options. Pass the merged configuration:

       if (newSession?.status === 'pending') {
-        warnMissingPendingTaskHandlers({ ...this.#options, ...params });
+        warnMissingPendingTaskHandlers({ ...this.#options, ...params });
       }

1201-1204: Fix parameter destructuring and type safety.

The current string session handling uses unsafe type assertion. Improve type safety:

-  public setActive = async (params: SetActiveParams): Promise<void> => {
-    const { organization, beforeEmit, redirectUrl, navigate: setActiveNavigate } = params;
-    let { session } = params;
-    this.__internal_setActiveInProgress = true;
+  public setActive = async (params: SetActiveParams): Promise<void> => {
+    const { organization, beforeEmit, redirectUrl, navigate: setActiveNavigate } = params;
+    let { session } = params;
+    this.__internal_setActiveInProgress = true;

For the string session handling (lines 1217-1219):

       if (typeof session === 'string') {
-        session = (this.client.sessions.find(x => x.id === session) as SignedInSessionResource) || null;
+        const foundSession = this.client.sessions.find(x => x.id === session);
+        if (!foundSession) {
+          throw new Error(`Session with ID "${session}" not found.`);
+        }
+        session = foundSession as SignedInSessionResource;
       }
🧹 Nitpick comments (2)
packages/nextjs/src/client-boundary/controlComponents.ts (1)

13-13: Add a minimal smoke test to assert the new export is available.

No tests appear to cover this rename from the Next.js surface. Add a small test to prevent regressions.

Example:

// packages/nextjs/__tests__/client-boundary/controlComponents.test.ts
import { RedirectToTasks } from '../../src/client-boundary/controlComponents';

describe('controlComponents exports', () => {
  it('exposes RedirectToTasks', () => {
    expect(RedirectToTasks).toBeDefined();
  });
});

I can add this test (and optionally a temporary alias test if you keep BC for one cycle). Do you want me to open a follow-up PR?

packages/clerk-js/src/ui/contexts/components/SignIn.ts (1)

40-40: Remove unused destructured variables.

The basePath variable is destructured but not used in the current implementation. This creates unnecessary clutter.

-  const { navigate, basePath } = useRouter();
+  const { navigate } = useRouter();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d005e2d and fa3a908.

📒 Files selected for processing (9)
  • packages/clerk-js/bundlewatch.config.json (1 hunks)
  • packages/clerk-js/src/core/clerk.ts (22 hunks)
  • packages/clerk-js/src/core/sessionTasks.ts (1 hunks)
  • packages/clerk-js/src/ui/common/redirects.ts (1 hunks)
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts (4 hunks)
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts (4 hunks)
  • packages/clerk-js/src/utils/index.ts (1 hunks)
  • packages/clerk-js/src/utils/redirects.ts (1 hunks)
  • packages/nextjs/src/client-boundary/controlComponents.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/clerk-js/bundlewatch.config.json
  • packages/clerk-js/src/ui/contexts/components/SignUp.ts
  • packages/clerk-js/src/utils/redirects.ts
  • packages/clerk-js/src/core/sessionTasks.ts
🧰 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/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.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/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.ts
packages/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/utils/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.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/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.ts
packages/**/index.{js,ts}

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

Use tree-shaking friendly exports

Files:

  • packages/clerk-js/src/utils/index.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/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.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/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.ts
**/index.ts

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

Use index.ts files for clean imports but avoid deep barrel exports

Avoid barrel files (index.ts re-exports) as they can cause circular dependencies

Files:

  • packages/clerk-js/src/utils/index.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/index.ts
  • packages/nextjs/src/client-boundary/controlComponents.ts
  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
  • packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/ui/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)

packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure

Files:

  • packages/clerk-js/src/ui/contexts/components/SignIn.ts
  • packages/clerk-js/src/ui/common/redirects.ts
🧠 Learnings (1)
📚 Learning: 2025-08-08T19:00:08.931Z
Learnt from: LauraBeatris
PR: clerk/javascript#6486
File: packages/clerk-js/src/core/clerk.ts:1303-1344
Timestamp: 2025-08-08T19:00:08.931Z
Learning: In Clerk's navigation system for pending sessions, `taskUrls` (configured at the ClerkProvider level) should take priority over the `navigate` callback (from All-In-One components). This ensures that explicit provider-level configuration overrides component-level navigation logic, providing developers with predictable control over navigation behavior.

Applied to files:

  • packages/clerk-js/src/core/clerk.ts
🧬 Code Graph Analysis (2)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (6)
packages/types/src/session.ts (1)
  • SessionResource (208-261)
packages/clerk-js/src/ui/types.ts (1)
  • SignInCtx (64-68)
packages/clerk-js/src/ui/router/RouteContext.tsx (1)
  • useRouter (33-39)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/clerk-js/src/core/sessionTasks.ts (2)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskRedirectUrl (19-33)
packages/clerk-js/src/core/clerk.ts (7)
packages/clerk-js/src/utils/componentGuards.ts (1)
  • isSignedInAndSingleSessionModeEnabled (9-11)
packages/types/src/clerk.ts (1)
  • SetActiveParams (1189-1235)
packages/react/src/isomorphicClerk.ts (1)
  • session (672-678)
packages/types/src/session.ts (2)
  • SignedInSessionResource (285-285)
  • SessionResource (208-261)
packages/clerk-js/src/core/sessionTasks.ts (3)
  • warnMissingPendingTaskHandlers (49-63)
  • INTERNAL_SESSION_TASK_ROUTE_BY_KEY (10-12)
  • buildTaskUrl (38-47)
packages/clerk-js/src/core/resources/Session.ts (1)
  • currentTask (390-393)
packages/clerk-js/src/utils/runtime.ts (1)
  • inBrowser (1-3)
⏰ 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: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (10)
packages/nextjs/src/client-boundary/controlComponents.ts (1)

13-13: Rename to RedirectToTasks is correct and consistent with the PR direction.

The re-export aligns with the cross-package rename and keeps Next.js consumers in sync with @clerk/clerk-react.

packages/clerk-js/src/utils/index.ts (1)

24-24: LGTM! Clean barrel export addition.

The addition of export * from './redirects' is appropriate and follows the established pattern in the file. This properly exposes the new redirect utilities to consumers of the utils module.

packages/clerk-js/src/ui/contexts/components/SignIn.ts (1)

132-140: Ignore the newSession substitution suggestion—newSession isn’t in scope here
The SignIn.ts component only ever references clerk.session; there are no newSession variables or props passed into this context. If you need to compute taskUrl based on the pending session rather than the current one, you’ll first have to expose newSession through your UI context or perform the URL computation upstream where newSession is available.

Likely an incorrect or invalid review comment.

packages/clerk-js/src/ui/common/redirects.ts (2)

1-1: LGTM! Centralized redirect logic.

Good refactor to use the centralized buildRedirectUrl from the utils module instead of maintaining duplicate URL-building logic.


3-3: LGTM! Updated imports align with context refactoring.

The import path change reflects the broader refactoring to centralize context types, which improves maintainability.

packages/clerk-js/src/core/clerk.ts (5)

64-64: LGTM! Type import update aligns with session task changes.

The import of SessionResource instead of other session types aligns with the broader refactoring to use unified session types for task handling.


112-112: LGTM! Component guard consolidation.

Using isSignedInAndSingleSessionModeEnabled consistently across both openSignIn and openSignUp methods improves maintainability.


154-154: LGTM! Session task utilities import.

The import of session task utilities (buildTaskUrl, INTERNAL_SESSION_TASK_ROUTE_BY_KEY, warnMissingPendingTaskHandlers) supports the new per-task routing model.


1554-1574: LGTM! Well-implemented task URL builder.

The buildTasksUrl method properly:

  • Checks for current task existence
  • Prioritizes custom task URLs from options
  • Falls back to building default URLs
  • Uses appropriate URL building utilities

1666-1671: LGTM! Consistent redirect method.

The redirectToTasks method follows the same pattern as other redirect methods and properly delegates to buildTasksUrl.

Comment on lines +1854 to +1869
const setActiveNavigate = async ({
session,
baseUrl,
redirectUrl,
}: {
session: SessionResource;
baseUrl: string;
redirectUrl: string;
}) => {
if (!session.currentTask) {
await this.navigate(redirectUrl);
return;
}

await navigate(params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl }));
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve task navigation in OAuth callback.

The current implementation doesn't preserve redirectUrl when navigating to tasks. Also consider using the centralized task URL building:

     const setActiveNavigate = async ({
       session,
       baseUrl,
       redirectUrl,
     }: {
       session: SessionResource;
       baseUrl: string;
       redirectUrl: string;
     }) => {
       if (!session.currentTask) {
         await this.navigate(redirectUrl);
         return;
       }

-      await navigate(params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl }));
+      const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key];
+      const taskUrl = customTaskUrl ?? params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl });
+      await navigate(taskUrl);
     };
📝 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 setActiveNavigate = async ({
session,
baseUrl,
redirectUrl,
}: {
session: SessionResource;
baseUrl: string;
redirectUrl: string;
}) => {
if (!session.currentTask) {
await this.navigate(redirectUrl);
return;
}
await navigate(params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl }));
};
const setActiveNavigate = async ({
session,
baseUrl,
redirectUrl,
}: {
session: SessionResource;
baseUrl: string;
redirectUrl: string;
}) => {
if (!session.currentTask) {
await this.navigate(redirectUrl);
return;
}
const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key];
const taskUrl = customTaskUrl ?? params.taskUrl ?? buildTaskUrl(session.currentTask, { baseUrl });
await navigate(taskUrl);
};
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/clerk.ts around lines 1854 to 1869, the task
navigation handler drops the original redirectUrl and references an undefined
params variable; update it to preserve redirectUrl and use the centralized task
URL builder by calling the shared buildTaskUrl with both baseUrl and redirectUrl
(e.g. buildTaskUrl(session.currentTask, { baseUrl, redirectUrl })) and remove
the undefined params usage so the navigate call consistently navigates to the
constructed task URL when session.currentTask exists.

Comment on lines +2167 to +2178
const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
if (!session.currentTask) {
await this.navigate(redirectUrl);
return;
}

await this.navigate(
buildTaskUrl(session.currentTask, {
baseUrl: displayConfig.signInUrl,
}),
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Align Web3 navigation with OAuth pattern.

The Web3 flow should also respect provider-level taskUrls configuration like the OAuth flow:

     const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
       if (!session.currentTask) {
         await this.navigate(redirectUrl);
         return;
       }

-      await this.navigate(
-        buildTaskUrl(session.currentTask, {
-          baseUrl: displayConfig.signInUrl,
-        }),
-      );
+      const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key];
+      const taskUrl = customTaskUrl ?? buildTaskUrl(session.currentTask, {
+        baseUrl: displayConfig.signInUrl,
+      });
+      await this.navigate(taskUrl);
     };
📝 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 setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
if (!session.currentTask) {
await this.navigate(redirectUrl);
return;
}
await this.navigate(
buildTaskUrl(session.currentTask, {
baseUrl: displayConfig.signInUrl,
}),
);
};
const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
if (!session.currentTask) {
await this.navigate(redirectUrl);
return;
}
const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key];
const taskUrl = customTaskUrl ?? buildTaskUrl(session.currentTask, {
baseUrl: displayConfig.signInUrl,
});
await this.navigate(taskUrl);
};
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/clerk.ts around lines 2167 to 2178, the Web3
navigation always builds the task URL using displayConfig.signInUrl and ignores
provider-level taskUrls like the OAuth flow; update the logic so when
session.currentTask exists you first check the session's active provider (or
provider for the session) for a matching taskUrls entry and navigate to that URL
if present, otherwise fall back to buildTaskUrl(...) with
displayConfig.signInUrl; keep the existing early-return that navigates to
redirectUrl when there is no currentTask, and ensure you await the navigate
call.

Comment on lines +123 to +130
const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
const currentTask = session.currentTask;
if (!currentTask) {
return navigate(redirectUrl);
}

return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
};
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

Critical issues: Remove debug code and fix navigation logic.

Multiple issues need addressing:

  1. Lint error: The debugger statement violates ESLint rules
  2. Incorrect task URL construction: Manual path joining with basePath is error-prone
  3. Missing provider-level taskUrls support: Should respect ClerkProvider's taskUrls configuration
  4. Poor fallback handling: No fallback when redirectUrl is missing for active sessions
-  const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
-    debugger;
-    const currentTask = session.currentTask;
-    if (!currentTask) {
-      return navigate(redirectUrl);
-    }
-
-    return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
-  };
+  const navigateOnSetActive = useCallback(
+    async ({ session, redirectUrl }: { session: SessionResource; redirectUrl?: string }) => {
+      const currentTask = session.currentTask;
+      if (!currentTask) {
+        // No pending task: use provided redirectUrl or fall back to afterSignInUrl
+        return navigate(redirectUrl || afterSignInUrl);
+      }
+      // Pending task: prefer provider override, otherwise build the default task URL
+      const customTaskUrl = clerk.__internal_getOption('taskUrls')?.[currentTask.key];
+      const target = customTaskUrl ?? buildTaskRedirectUrl(currentTask, {
+        routing: ctx.routing,
+        baseUrl: signInUrl,
+        authQueryString,
+        path: ctx.path,
+      });
+      return navigate(target);
+    },
+    [navigate, clerk, signInUrl, afterSignInUrl, ctx.routing, authQueryString, ctx.path],
+  );

Add missing import:

-import { createContext, useContext, useMemo } from 'react';
+import { createContext, useContext, useMemo, useCallback } from 'react';

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/contexts/components/SignIn.ts around lines 123-130,
remove any leftover debugger statements, stop manually concatenating paths with
basePath, and instead construct task URLs via the provider-level taskUrls
mapping (falling back to INTERNAL_SESSION_TASK_ROUTE_BY_KEY when provider
mapping missing); ensure you read the taskUrls from ClerkProvider context and
call the shared path-building utility (add the missing import for that utility)
to produce a correct absolute URL, and if redirectUrl is absent for an active
session fall back to a safe default (e.g., provider taskUrl fallback or '/')
before calling navigate.

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.

4 participants