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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/polite-cars-lose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': patch
---

Fix `useClearQueriesOnSignOut` hook by removing conditional `useEffect`
5 changes: 5 additions & 0 deletions .changeset/remove-swr-switches.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': major
---

Remove SWR hooks and env-based switchovers in favor of the React Query implementations; promote @tanstack/query-core to a runtime dependency.
2 changes: 1 addition & 1 deletion packages/clerk-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"@solana/wallet-standard": "catalog:module-manager",
"@stripe/stripe-js": "5.6.0",
"@swc/helpers": "catalog:repo",
"@tanstack/query-core": "5.87.4",
"@tanstack/query-core": "5.90.16",
"@wallet-standard/core": "catalog:module-manager",
"@zxcvbn-ts/core": "catalog:module-manager",
"@zxcvbn-ts/language-common": "catalog:module-manager",
Expand Down
8 changes: 1 addition & 7 deletions packages/clerk-js/src/test/create-fixtures.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// @ts-nocheck

import type { ClerkOptions, ClientJSON, EnvironmentJSON, LoadedClerk } from '@clerk/shared/types';
import { useState } from 'react';
import { vi } from 'vitest';

import { Clerk as ClerkCtor } from '@/core/clerk';
Expand Down Expand Up @@ -87,7 +86,6 @@ const unboundCreateFixtures = (

const MockClerkProvider = (props: any) => {
const { children } = props;
const [swrConfig] = useState(() => ({ provider: () => new Map() }));

const componentsWithoutContext = [
'UsernameSection',
Expand All @@ -108,11 +106,7 @@ const unboundCreateFixtures = (
);

return (
<CoreClerkContextWrapper
clerk={clerkMock}
// Clear swr cache
swrConfig={swrConfig}
>
<CoreClerkContextWrapper clerk={clerkMock}>
<EnvironmentProvider value={environmentMock}>
<OptionsProvider value={optionsMock}>
<RouteContext.Provider value={routerMock}>
Expand Down
1 change: 0 additions & 1 deletion packages/shared/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ declare const JS_PACKAGE_VERSION: string;
declare const UI_PACKAGE_VERSION: string;
declare const __DEV__: boolean;
declare const __BUILD_DISABLE_RHC__: boolean;
declare const __CLERK_USE_RQ__: boolean;

interface ImportMetaEnv {
readonly [key: string]: string;
Expand Down
5 changes: 2 additions & 3 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@
"test:coverage": "vitest --collectCoverage && open coverage/lcov-report/index.html"
},
"dependencies": {
"@tanstack/query-core": "5.90.16",
"dequal": "2.0.3",
"glob-to-regexp": "0.4.1",
"js-cookie": "3.0.5",
"std-env": "^3.9.0",
"swr": "2.3.4"
"std-env": "^3.9.0"
},
"devDependencies": {
"@base-org/account": "catalog:module-manager",
Expand All @@ -138,7 +138,6 @@
"@solana/wallet-standard": "catalog:module-manager",
"@stripe/react-stripe-js": "3.1.1",
"@stripe/stripe-js": "5.6.0",
"@tanstack/query-core": "5.87.4",
"@types/glob-to-regexp": "0.4.4",
"@types/js-cookie": "3.0.6",
"@wallet-standard/core": "catalog:module-manager",
Expand Down

This file was deleted.

This file was deleted.

78 changes: 76 additions & 2 deletions packages/shared/src/react/billing/useInitializePaymentMethod.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,76 @@
export type { UseInitializePaymentMethodResult } from 'virtual:data-hooks/useInitializePaymentMethod';
export { __internal_useInitializePaymentMethod } from 'virtual:data-hooks/useInitializePaymentMethod';
import { useCallback, useMemo } from 'react';

import type { BillingInitializedPaymentMethodResource, ForPayerType } from '../../types';
import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
import { useClerkQueryClient } from '../clerk-rq/use-clerk-query-client';
import { useClerkQuery } from '../clerk-rq/useQuery';
import { useOrganizationContext, useUserContext } from '../contexts';
import { useBillingHookEnabled } from '../hooks/useBillingHookEnabled';

type InitializePaymentMethodOptions = {
for?: ForPayerType;
};

export type UseInitializePaymentMethodResult = {
initializedPaymentMethod: BillingInitializedPaymentMethodResource | undefined;
initializePaymentMethod: () => Promise<BillingInitializedPaymentMethodResource | undefined>;
};

/**
* @internal
*/
function useInitializePaymentMethod(options?: InitializePaymentMethodOptions): UseInitializePaymentMethodResult {
const { for: forType } = options ?? {};
const { organization } = useOrganizationContext();
const user = useUserContext();

const resource = forType === 'organization' ? organization : user;

const billingEnabled = useBillingHookEnabled(options);

const queryKey = useMemo(() => {
return ['billing-payment-method-initialize', { resourceId: resource?.id }] as const;
}, [resource?.id]);

const isEnabled = Boolean(resource?.id) && billingEnabled;

const query = useClerkQuery({
queryKey,
queryFn: async () => {
if (!resource) {
return undefined;
}

return resource.initializePaymentMethod({
gateway: 'stripe',
});
},
enabled: isEnabled,
staleTime: 1_000 * 60,
refetchOnWindowFocus: false,
placeholderData: defineKeepPreviousDataFn(true),
});

const [queryClient] = useClerkQueryClient();

const initializePaymentMethod = useCallback(async () => {
if (!resource) {
return undefined;
}

const result = await resource.initializePaymentMethod({
gateway: 'stripe',
});

queryClient.setQueryData(queryKey, result);

return result;
}, [queryClient, queryKey, resource]);

return {
initializedPaymentMethod: query.data ?? undefined,
initializePaymentMethod,
};
}
Comment on lines +22 to +74
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for useClearQueriesOnSignOut usage in billing hooks
rg -n "useClearQueriesOnSignOut" packages/shared/src/react/billing/ packages/shared/src/react/hooks/

Repository: clerk/javascript

Length of output: 2757


🏁 Script executed:

#!/bin/bash
# Check usePaymentAttemptQuery to understand why it uses useClearQueriesOnSignOut
cat -n packages/shared/src/react/hooks/usePaymentAttemptQuery.tsx

Repository: clerk/javascript

Length of output: 2472


🏁 Script executed:

#!/bin/bash
# Also check the full useInitializePaymentMethod implementation with imports
cd packages/shared/src/react/billing
cat -n useInitializePaymentMethod.tsx | head -80

Repository: clerk/javascript

Length of output: 2884


🏁 Script executed:

#!/bin/bash
# Check what BillingInitializedPaymentMethodResource contains
rg -A 10 "BillingInitializedPaymentMethodResource" packages/shared/src/types/ | head -30

Repository: clerk/javascript

Length of output: 2248


🏁 Script executed:

#!/bin/bash
# Check if there's any other sign-out handling or cache invalidation logic in useInitializePaymentMethod context
rg -n "sign.*out|Sign.*Out" packages/shared/src/react/billing/ packages/shared/src/react/hooks/useBillingHookEnabled.tsx

Repository: clerk/javascript

Length of output: 157


🏁 Script executed:

#!/bin/bash
# Search for useBillingHookEnabled to understand if it has special sign-out logic
find packages/shared/src -name "*useBillingHookEnabled*"

Repository: clerk/javascript

Length of output: 116


🏁 Script executed:

#!/bin/bash
# Check other billing hooks to see the pattern of useClearQueriesOnSignOut
grep -l "useClearQueriesOnSignOut" packages/shared/src/react/hooks/*.tsx

Repository: clerk/javascript

Length of output: 279


🏁 Script executed:

#!/bin/bash
# Check useBillingHookEnabled
cat -n packages/shared/src/react/hooks/useBillingHookEnabled.ts

Repository: clerk/javascript

Length of output: 1394


🏁 Script executed:

#!/bin/bash
# Check useSubscription to see the full pattern
cat -n packages/shared/src/react/hooks/useSubscription.tsx

Repository: clerk/javascript

Length of output: 3261


Add useClearQueriesOnSignOut to clear cached payment method data on sign-out.

The hook caches the payment method initialization result containing externalClientSecret (a sensitive Stripe client secret) but doesn't clear this cache when the user signs out. This is inconsistent with other billing hooks (usePaymentAttemptQuery, useSubscription) which explicitly call useClearQueriesOnSignOut({isSignedOut: user === null, authenticated, stableKeys: stableKey}) to clear sensitive cached data. Add this call to prevent the client secret from persisting in the query cache after sign-out.

🤖 Prompt for AI Agents
In @packages/shared/src/react/billing/useInitializePaymentMethod.tsx around
lines 22 - 74, Call useClearQueriesOnSignOut to purge the cached
initializePaymentMethod result on sign-out: import useClearQueriesOnSignOut and
invoke it in useInitializePaymentMethod (after you compute queryKey / query)
with isSignedOut: user === null, authenticated: the authenticated value from
useUserContext (or equivalent boolean available from user context), and
stableKeys: queryKey (or the stableKey used by other billing hooks). This
ensures the sensitive externalClientSecret in initializedPaymentMethod is
removed from the query cache on sign-out.


export { useInitializePaymentMethod as __internal_useInitializePaymentMethod };
37 changes: 0 additions & 37 deletions packages/shared/src/react/billing/useStripeClerkLibs.rq.tsx

This file was deleted.

39 changes: 0 additions & 39 deletions packages/shared/src/react/billing/useStripeClerkLibs.swr.tsx

This file was deleted.

Loading
Loading