-
Notifications
You must be signed in to change notification settings - Fork 419
feat(shared): Remove SWR #7455
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
base: main
Are you sure you want to change the base?
feat(shared): Remove SWR #7455
Conversation
🦋 Changeset detectedLatest commit: ca51690 The changes in this PR will be included in the next version bump. This PR includes changesets to release 19 packages
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR removes SWR variants and the environment-based switchover, consolidating React Query implementations as the single data-fetching layer. It promotes Changes
Pre-merge checks✅ Passed checks (5 passed)
📜 Recent review detailsConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Disabled knowledge base sources:
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used📓 Path-based instructions (5)**/*.{js,jsx,ts,tsx,json,md,yml,yaml}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
packages/*/package.json📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
**/package.json📄 CodeRabbit inference engine (.cursor/rules/global.mdc)
Files:
**/*.{js,ts,jsx,tsx,json,md,yml,yaml}📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Files:
**/*⚙️ CodeRabbit configuration file
Files:
⏰ 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). (25)
Comment |
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Nitpick comments (14)
packages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsx (1)
508-538: LGTM! Consider simplifying the test name.The expectation of at least 2 fetcher calls correctly reflects React Query's behavior where revalidating the paginated query also invalidates the infinite query variant sharing the same cache key base.
Since SWR has been completely removed, consider simplifying the test name by removing the now-redundant phrase "only for React Query":
- it('revalidate propagates to infinite counterpart only for React Query', async () => { + it('revalidate propagates to infinite counterpart', async () => {packages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsx (3)
64-64: Update test name to reflect SWR removal.The test name mentions "only when using React Query" but since SWR has been completely removed in this PR, React Query is now always used. The conditional phrasing is misleading.
Consider updating the test name:
- it('cascades revalidation for related queries only when using React Query', async () => { + it('cascades revalidation for related queries', async () => {
122-123: Remove redundant assertion.Line 122 checks for
≥ 1call, but line 123 immediately checks for≥ 2calls. The first assertion is redundant since≥ 2already implies≥ 1.Apply this diff:
- await waitFor(() => expect(getAllSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); await waitFor(() => expect(getAllSpy.mock.calls.length).toBeGreaterThanOrEqual(2));
153-154: Remove redundant assertion.Same issue as lines 122-123: line 153 checks for
≥ 1call, but line 154 immediately checks for≥ 2calls. The first assertion is redundant.Apply this diff:
- await waitFor(() => expect(getAllSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); await waitFor(() => expect(getAllSpy.mock.calls.length).toBeGreaterThanOrEqual(2));packages/shared/src/react/hooks/usePlanDetailsQuery.tsx (1)
8-11: Enhance JSDoc for better maintainability.While this is an internal API, adding
@param,@returns, and a brief description would help maintainers understand the hook's purpose and usage, especially given the migration from SWR to React Query.Consider applying this enhancement:
/** * @internal + * Internal hook to fetch plan details using React Query. + * @param params - Configuration for the plan details query + * @param params.planId - The ID of the plan to fetch + * @param params.initialPlan - Initial plan data to use before fetching + * @param params.keepPreviousData - Whether to keep previous data during refetches (default: true) + * @returns Plan details query result with data, error, loading and fetching states */ export function __internal_usePlanDetailsQuery(params: UsePlanDetailsQueryParams = {}): PlanDetailsQueryResult {packages/shared/src/react/hooks/useStatementQuery.tsx (1)
1-26: LGTM! Clean setup with minor optimization opportunity.The imports, parameter handling, and context setup are well-structured. Type-only imports follow best practices for tree-shaking.
Optional refinement: Line 28 passes the entire
paramsobject touseBillingHookEnabled, which only expects{ for?, enabled?, authenticated? }. While TypeScript's structural typing allows this, you could destructure only the needed fields for clarity:-const billingEnabled = useBillingHookEnabled(params); +const billingEnabled = useBillingHookEnabled({ for: forType, enabled: params.enabled });packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.ts (4)
91-93: Stale comment references SWR.The comment "wait until SWR mock finishes fetching" is outdated since SWR has been removed. Consider updating it to reflect the React Query implementation.
- // wait until SWR mock finishes fetching + // wait until data finishes fetching await waitFor(() => expect(result.current.isLoading).toBe(false));
206-211: Stale comment references SWR.Similar to the earlier comment, this reference to "SWR may refetch" should be updated since the implementation is now React Query-based.
- // SWR may refetch the first page after size change; ensure both pages 1 and 2 were requested + // React Query may refetch the first page after size change; ensure both pages 1 and 2 were requested expect(fetcher.mock.calls.length).toBeGreaterThanOrEqual(2);
241-246: Stale comment references SWR mock.This comment references "our SWR mock" which is no longer accurate.
- // our SWR mock sets loading=false if key is null and not calling fetcher + // loading is false when disabled and fetcher is not called expect(fetcher).toHaveBeenCalledTimes(0);
677-680: Stale comment references SWR.This comment mentions "SWR refetches all pages in infinite mode" but should reference React Query.
- // Verify both pages were revalidated (SWR refetches all pages in infinite mode) + // Verify both pages were revalidated (React Query refetches all pages in infinite mode) const revalidatedPages = revalidateCalls.map(c => c.page);packages/shared/src/react/hooks/usePaymentAttemptQuery.tsx (1)
32-43: Harden sign-out detection +queryKeyshape assumptions.
isSignedOut: user === nullwon’t trigger cleanup if the context ever yieldsundefined, andqueryKey[3].argswill throw if the key shape changes.- useClearQueriesOnSignOut({ - isSignedOut: user === null, // works with the transitive state + useClearQueriesOnSignOut({ + isSignedOut: user == null, // null or undefined authenticated, stableKeys: stableKey, }); const query = useClerkQuery({ queryKey, queryFn: ({ queryKey }) => { - const args = queryKey[3].args; + const args = (queryKey as typeof queryKey)[3]?.args; return clerk.billing.getPaymentAttempt(args); },packages/shared/src/react/hooks/usePagesOrInfinite.tsx (1)
58-73: Avoidreturn undefined as anyinqueryFn(make the invariant explicit).
Iffetcheris unexpectedly missing, you’ll silently cacheundefinedinstead of surfacing a real misuse.queryFn: ({ queryKey }) => { const { args } = queryKey[3]; if (!fetcher) { - return undefined as any; + throw new Error('usePagesOrInfinite: `fetcher` is required when queries are enabled.'); } return fetcher(args); },Also applies to: 90-96
packages/shared/src/react/billing/useStripeClerkLibs.tsx (1)
19-37: Consider exposing error state or adding error handling.The hook returns
nullboth when billing is disabled and when the query fails. Consumers cannot distinguish between "not yet loaded" and "failed to load" states.If error handling is intentionally omitted for simplicity, this is acceptable for an internal hook. Otherwise, consider returning
query.erroror a loading state for better observability.-export type UseStripeClerkLibsResult = StripeClerkLibs | null; +export type UseStripeClerkLibsResult = { + data: StripeClerkLibs | null; + isLoading: boolean; + error: Error | null; +};packages/shared/src/react/billing/useInitializePaymentMethod.tsx (1)
54-73: Consider handling errors frominitializePaymentMethod.The callback propagates any thrown errors to callers, which is acceptable. However, if the API call fails, the query cache won't be updated, and consumers might see stale data. Consider whether failed attempts should invalidate the cache or if error state should be tracked.
If error handling is desired:
const initializePaymentMethod = useCallback(async () => { if (!resource) { return undefined; } - const result = await resource.initializePaymentMethod({ - gateway: 'stripe', - }); + try { + const result = await resource.initializePaymentMethod({ + gateway: 'stripe', + }); - queryClient.setQueryData(queryKey, result); + queryClient.setQueryData(queryKey, result); - return result; + return result; + } catch (error) { + queryClient.invalidateQueries({ queryKey }); + throw error; + } }, [queryClient, queryKey, resource]);
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (45)
.changeset/remove-swr-switches.md(1 hunks)packages/shared/global.d.ts(0 hunks)packages/shared/package.json(1 hunks)packages/shared/src/react/billing/useInitializePaymentMethod.rq.tsx(0 hunks)packages/shared/src/react/billing/useInitializePaymentMethod.swr.tsx(0 hunks)packages/shared/src/react/billing/useInitializePaymentMethod.tsx(1 hunks)packages/shared/src/react/billing/useStripeClerkLibs.rq.tsx(0 hunks)packages/shared/src/react/billing/useStripeClerkLibs.swr.tsx(0 hunks)packages/shared/src/react/billing/useStripeClerkLibs.tsx(1 hunks)packages/shared/src/react/billing/useStripeLoader.rq.tsx(0 hunks)packages/shared/src/react/billing/useStripeLoader.swr.tsx(0 hunks)packages/shared/src/react/billing/useStripeLoader.tsx(1 hunks)packages/shared/src/react/clerk-swr.ts(0 hunks)packages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsx(2 hunks)packages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsx(3 hunks)packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.ts(1 hunks)packages/shared/src/react/hooks/__tests__/usePlans.spec.tsx(1 hunks)packages/shared/src/react/hooks/__tests__/useSubscription.spec.tsx(3 hunks)packages/shared/src/react/hooks/createBillingPaginatedHook.tsx(1 hunks)packages/shared/src/react/hooks/index.ts(1 hunks)packages/shared/src/react/hooks/useAPIKeys.swr.tsx(0 hunks)packages/shared/src/react/hooks/useAPIKeys.ts(0 hunks)packages/shared/src/react/hooks/usePagesOrInfinite.rq.tsx(0 hunks)packages/shared/src/react/hooks/usePagesOrInfinite.swr.tsx(0 hunks)packages/shared/src/react/hooks/usePagesOrInfinite.tsx(1 hunks)packages/shared/src/react/hooks/usePaymentAttemptQuery.rq.tsx(0 hunks)packages/shared/src/react/hooks/usePaymentAttemptQuery.swr.tsx(0 hunks)packages/shared/src/react/hooks/usePaymentAttemptQuery.tsx(1 hunks)packages/shared/src/react/hooks/usePlanDetailsQuery.rq.tsx(0 hunks)packages/shared/src/react/hooks/usePlanDetailsQuery.swr.tsx(0 hunks)packages/shared/src/react/hooks/usePlanDetailsQuery.tsx(1 hunks)packages/shared/src/react/hooks/useStatementQuery.rq.tsx(0 hunks)packages/shared/src/react/hooks/useStatementQuery.swr.tsx(0 hunks)packages/shared/src/react/hooks/useStatementQuery.tsx(1 hunks)packages/shared/src/react/hooks/useSubscription.rq.tsx(0 hunks)packages/shared/src/react/hooks/useSubscription.swr.tsx(0 hunks)packages/shared/src/react/hooks/useSubscription.tsx(1 hunks)packages/shared/src/react/providers/SWRConfigCompat.rq.tsx(0 hunks)packages/shared/src/react/providers/SWRConfigCompat.swr.tsx(0 hunks)packages/shared/src/react/providers/SWRConfigCompat.tsx(1 hunks)packages/shared/src/types/virtual-data-hooks.d.ts(0 hunks)packages/shared/tsconfig.json(1 hunks)packages/shared/tsdown.config.mts(0 hunks)packages/shared/vitest.config.mts(0 hunks)packages/shared/vitest.setup.mts(0 hunks)
💤 Files with no reviewable changes (26)
- packages/shared/global.d.ts
- packages/shared/src/react/hooks/useAPIKeys.ts
- packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
- packages/shared/vitest.setup.mts
- packages/shared/src/react/hooks/useStatementQuery.rq.tsx
- packages/shared/src/react/clerk-swr.ts
- packages/shared/src/react/hooks/useSubscription.rq.tsx
- packages/shared/src/react/hooks/useSubscription.swr.tsx
- packages/shared/src/react/hooks/usePlanDetailsQuery.rq.tsx
- packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
- packages/shared/vitest.config.mts
- packages/shared/tsdown.config.mts
- packages/shared/src/react/hooks/usePaymentAttemptQuery.swr.tsx
- packages/shared/src/react/hooks/usePagesOrInfinite.swr.tsx
- packages/shared/src/react/billing/useInitializePaymentMethod.swr.tsx
- packages/shared/src/react/hooks/useAPIKeys.swr.tsx
- packages/shared/src/react/billing/useStripeClerkLibs.rq.tsx
- packages/shared/src/react/hooks/usePaymentAttemptQuery.rq.tsx
- packages/shared/src/react/hooks/usePagesOrInfinite.rq.tsx
- packages/shared/src/react/billing/useStripeLoader.rq.tsx
- packages/shared/src/react/hooks/usePlanDetailsQuery.swr.tsx
- packages/shared/src/react/hooks/useStatementQuery.swr.tsx
- packages/shared/src/react/billing/useStripeClerkLibs.swr.tsx
- packages/shared/src/react/billing/useStripeLoader.swr.tsx
- packages/shared/src/react/billing/useInitializePaymentMethod.rq.tsx
- packages/shared/src/types/virtual-data-hooks.d.ts
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
All code must pass ESLint checks with the project's configuration
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/tsconfig.jsonpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/package.jsonpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
packages/**/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Follow established naming conventions (PascalCase for components, camelCase for variables)
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
packages/**/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
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
Implement proper logging with different levels
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.ts?(x)
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use real Clerk instances for integration tests
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)
**/*.{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
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Implement type guards forunknowntypes using the patternfunction isType(value: unknown): value is Type
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details in classes
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like<T extends { id: string }>
Use utility types likeOmit,Partial, andPickfor data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Useconst assertionswithas constfor literal types
Usesatisfiesoperator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Use ESLint with custom configurations tailored for different package types
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{js,ts,jsx,tsx,json,md,yml,yaml}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Use Prettier for code formatting across all packages
Files:
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/tsconfig.jsonpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/package.jsonpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/index.ts
📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)
Avoid barrel files (index.ts re-exports) as they can cause circular dependencies
Files:
packages/shared/src/react/hooks/index.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.tsx: Use error boundaries in React components
Minimize re-renders in React components
**/*.tsx: Use proper type definitions for props and state in React components
Leverage TypeScript's type inference where possible in React components
Use proper event types for handlers in React components
Implement proper generic types for reusable React components
Use proper type guards for conditional rendering in React components
Files:
packages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{md,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Update documentation for API changes
Files:
packages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components (e.g.,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
Separate UI components from business logic components
Use useState for simple state management in React components
Use useReducer for complex state logic in React components
Implement proper state initialization in React components
Use proper state updates with callbacks in React components
Implement proper state cleanup in React components
Use Context API for theme/authentication state management
Implement proper state persistence in React applications
Use React.memo for expensive components
Implement proper useCallback for handlers in React components
Use proper useMemo for expensive computations in React components
Implement proper virtualization for lists in React components
Use proper code splitting with React.lazy in React applications
Implement proper cleanup in useEffect hooks
Use proper refs for DOM access in React components
Implement proper event listener cleanup in React components
Use proper abort controllers for fetch in React components
Implement proper subscription cleanup in React components
Use proper HTML elements for semantic HTML in React components
Implement proper ARIA attributes for accessibility in React components
Use proper heading hierarchy in React components
Implement proper form labels in React components
Use proper button types in React components
Implement proper focus management for keyboard navigation in React components
Use proper keyboard shortcuts in React components
Implement proper tab order in React components
Use proper ...
Files:
packages/shared/src/react/hooks/usePaymentAttemptQuery.tsxpackages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/providers/SWRConfigCompat.tsxpackages/shared/src/react/billing/useStripeClerkLibs.tsxpackages/shared/src/react/hooks/usePlanDetailsQuery.tsxpackages/shared/src/react/hooks/useStatementQuery.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/useSubscription.tsxpackages/shared/src/react/hooks/usePagesOrInfinite.tsxpackages/shared/src/react/billing/useInitializePaymentMethod.tsxpackages/shared/src/react/billing/useStripeLoader.tsxpackages/shared/src/react/hooks/createBillingPaginatedHook.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
**/*.{test,spec}.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.{test,spec}.{jsx,tsx}: Use React Testing Library for unit testing React components
Test component behavior, not implementation details
Use proper test queries in React Testing Library tests
Implement proper test isolation in React component tests
Use proper test coverage in React component tests
Test component interactions in integration tests
Use proper test data in React component tests
Implement proper test setup in React component tests
Use proper test cleanup in React component tests
Implement proper test assertions in React component tests
Use proper test structure for React component tests
Files:
packages/shared/src/react/hooks/__tests__/useAPIKeys.spec.tsxpackages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsxpackages/shared/src/react/hooks/__tests__/useSubscription.spec.tsxpackages/shared/src/react/hooks/__tests__/usePlans.spec.tsx
packages/*/package.json
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
packages/*/package.json: Packages should export TypeScript types alongside runtime code
Follow semantic versioning for all packagesAll packages must be published under @clerk namespace
packages/*/package.json: Framework packages should depend on@clerk/clerk-jsfor core functionality
@clerk/sharedshould be a common dependency for most packages in the monorepo
Files:
packages/shared/package.json
**/package.json
📄 CodeRabbit inference engine (.cursor/rules/global.mdc)
Use pnpm as the package manager for this monorepo
Files:
packages/shared/package.json
🧬 Code graph analysis (5)
packages/shared/src/react/hooks/usePaymentAttemptQuery.tsx (7)
packages/shared/src/react/hooks/usePaymentAttemptQuery.types.ts (2)
UsePaymentAttemptQueryParams(7-30)PaymentAttemptQueryResult(35-52)packages/shared/src/react/index.ts (3)
useClerkInstanceContext(12-12)useUserContext(18-18)useOrganizationContext(15-15)packages/shared/src/react/hooks/usePaymentAttemptQuery.shared.ts (1)
usePaymentAttemptQueryCacheKeys(7-32)packages/shared/src/react/hooks/useBillingHookEnabled.ts (1)
useBillingHookEnabled(7-27)packages/shared/src/react/hooks/useClearQueriesOnSignOut.ts (1)
useClearQueriesOnSignOut(28-64)packages/shared/src/react/clerk-rq/useQuery.ts (1)
useClerkQuery(39-41)packages/shared/src/react/clerk-rq/keep-previous-data.ts (1)
defineKeepPreviousDataFn(4-11)
packages/shared/src/react/billing/useStripeClerkLibs.tsx (4)
packages/shared/src/react/hooks/index.ts (1)
useClerk(10-10)packages/shared/src/react/hooks/useBillingHookEnabled.ts (1)
useBillingHookEnabled(7-27)packages/shared/src/react/clerk-rq/useQuery.ts (1)
useClerkQuery(39-41)packages/shared/src/react/clerk-rq/keep-previous-data.ts (1)
defineKeepPreviousDataFn(4-11)
packages/shared/src/react/hooks/useStatementQuery.tsx (6)
packages/shared/src/react/hooks/useStatementQuery.types.ts (2)
UseStatementQueryParams(6-29)StatementQueryResult(34-51)packages/shared/src/react/hooks/useStatementQuery.shared.ts (1)
useStatementQueryCacheKeys(7-32)packages/shared/src/react/hooks/useBillingHookEnabled.ts (1)
useBillingHookEnabled(7-27)packages/shared/src/react/hooks/useClearQueriesOnSignOut.ts (1)
useClearQueriesOnSignOut(28-64)packages/shared/src/react/clerk-rq/useQuery.ts (1)
useClerkQuery(39-41)packages/shared/src/react/clerk-rq/keep-previous-data.ts (1)
defineKeepPreviousDataFn(4-11)
packages/shared/src/react/billing/useStripeLoader.tsx (5)
packages/shared/src/react/billing/useStripeClerkLibs.tsx (1)
UseStripeClerkLibsResult(14-14)packages/clerk-js/src/core/resources/BillingCheckout.ts (1)
externalGatewayId(142-144)packages/shared/src/react/hooks/useBillingHookEnabled.ts (1)
useBillingHookEnabled(7-27)packages/shared/src/react/clerk-rq/useQuery.ts (1)
useClerkQuery(39-41)packages/shared/src/react/clerk-rq/keep-previous-data.ts (1)
defineKeepPreviousDataFn(4-11)
packages/shared/src/react/hooks/createBillingPaginatedHook.tsx (1)
packages/react/src/isomorphicClerk.ts (1)
organization(737-743)
⏰ 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). (29)
- GitHub Check: Unit Tests (shared, clerk-js, RQ)
- GitHub Check: Unit Tests (**)
- GitHub Check: Static analysis
- GitHub Check: Publish with pkg-pr-new
- GitHub Check: Integration Tests (quickstart, chrome, 15)
- GitHub Check: Integration Tests (nextjs, chrome, 16, RQ)
- GitHub Check: Integration Tests (quickstart, chrome, 16)
- GitHub Check: Integration Tests (nextjs, chrome, 16)
- GitHub Check: Integration Tests (nextjs, chrome, 15)
- GitHub Check: Integration Tests (custom, chrome)
- GitHub Check: Integration Tests (machine, chrome, RQ)
- GitHub Check: Integration Tests (handshake:staging, chrome)
- GitHub Check: Integration Tests (nuxt, chrome)
- GitHub Check: Integration Tests (react-router, chrome)
- GitHub Check: Integration Tests (billing, chrome)
- GitHub Check: Integration Tests (sessions:staging, chrome)
- GitHub Check: Integration Tests (billing, chrome, RQ)
- GitHub Check: Integration Tests (handshake, chrome)
- GitHub Check: Integration Tests (tanstack-react-start, chrome)
- GitHub Check: Integration Tests (generic, chrome)
- GitHub Check: Integration Tests (express, chrome)
- GitHub Check: Integration Tests (machine, chrome)
- GitHub Check: Integration Tests (astro, chrome)
- GitHub Check: Integration Tests (vue, chrome)
- GitHub Check: Integration Tests (sessions, chrome)
- GitHub Check: Integration Tests (ap-flows, chrome)
- GitHub Check: Integration Tests (localhost, chrome)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (22)
packages/shared/src/react/hooks/index.ts (1)
24-24: LGTM! Comment accurately reflects the SWR removal.The updated comment correctly documents that only React Query implementations are now centralized here, aligning with the PR's goal to completely remove SWR.
packages/shared/src/react/hooks/__tests__/createBillingPaginatedHook.spec.tsx (1)
311-311: LGTM! Correct expectation after sign-out.The unconditional expectation that
isLoadingisfalseafter sign-out is correct for React Query behavior. When the user becomes null, the authenticated hook correctly transitions to a non-loading state.packages/shared/src/react/hooks/usePlanDetailsQuery.tsx (2)
12-15: LGTM! Clean parameter handling and ID resolution.The use of nullish coalescing and optional chaining follows TypeScript best practices, and the fallback logic correctly prioritizes explicit
planIdoverinitialPlan.id.
17-37: Well-structured React Query configuration.The query setup is correct:
- Conditional execution via
enabledproperly gates the query- The defensive throw in
queryFnprovides type narrowing and safeguards against future logic changesinitialDataUpdatedAt: 0appropriately marks initial data as stale for refetchingplaceholderDatawithdefineKeepPreviousDataFnhandles thekeepPreviousDatabehavior correctlypackages/shared/src/react/hooks/useStatementQuery.tsx (2)
28-36: LGTM! Proper enable gating and cleanup.The query enable logic correctly combines
statementIdpresence with billing enablement. The sign-out cleanup properly removes cached queries using matching stable keys and authenticated flags.
38-49: LGTM! Solid React Query implementation.The query implementation is well-structured with appropriate caching strategy (1-minute stale time) and optional placeholder data for smooth UX during refetches.
The defensive
statementIdcheck on line 41 is necessary for TypeScript type narrowing, even though theenabledflag prevents the queryFn from executing whenstatementIdis falsy. Without this check, TypeScript would not narrowstatementIdfromstring | nulltostringfor the API call on line 44.packages/shared/src/react/hooks/createBillingPaginatedHook.tsx (1)
140-149: LGTM! Clean simplification of the cache key structure.The removal of the environment-based key selection (
_orgIdvsorgId) in favor of a consistentorgIdproperty aligns well with the PR's objective to eliminate__CLERK_USE_RQ__branching.packages/shared/tsconfig.json (1)
25-30: LGTM! Clean simplification of TypeScript configuration.The consolidation from multiple granular
virtual:data-hooks/*path mappings to a single@/*alias, along with removingvirtual-data-hooks.d.tsfrom includes, correctly reflects the removal of the virtual module infrastructure..changeset/remove-swr-switches.md (1)
1-5: Appropriate major version bump for breaking changes.The
majordesignation is correct given that:
- SWR-based hooks are being removed (breaking for any consumers using them)
@tanstack/query-coreis promoted to a runtime dependency (new peer requirement)The changeset description accurately summarizes the scope of changes.
packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.ts (1)
725-726: LGTM! Correct unconditional assertion after SWR removal.The test now unconditionally expects the fetcher to be called at least twice during revalidation cascading, which is the expected React Query behavior. This aligns with the removal of environment-based branching.
packages/shared/src/react/hooks/__tests__/useSubscription.spec.tsx (3)
104-112: Good: sign-out now always clearsdata(no env-branching).
This matches the PR goal of removing SWR-driven divergent behavior.
125-133: Good: sign-out clearsdataeven withkeepPreviousData=true.
This is an important regression guard now that only React Query behavior remains.
150-164: Good:isLoadingstays false during refetch whenkeepPreviousData=true.
This aligns with React Query’s typical “background refetch” semantics (isFetchingtrue whileisLoadingfalse).packages/shared/src/react/hooks/usePaymentAttemptQuery.tsx (1)
12-55: LGTM: consolidated RQ hook wiring + sign-out query cleanup is consistent with the new pattern.
Enablement (billingEnabled+ id present), cache keys, andstaleTimeall look reasonable.packages/shared/src/react/hooks/useSubscription.tsx (1)
38-76: Good: query enablement +keepPreviousData+ SWR-compatibleerrornormalization.
TheplaceholderData: defineKeepPreviousDataFn(keepPreviousData && queriesEnabled)bit is especially clean.packages/shared/src/react/billing/useStripeClerkLibs.tsx (3)
1-6: LGTM!The imports are well-organized with type-only imports properly separated, and the dependencies align with the hook's requirements.
8-14: LGTM!Type definitions are clean with proper use of
typeoffor deriving the function type and explicit export of the result type for consumers.
39-39: LGTM!The internal alias export pattern aligns with the PR's migration approach and the
@internalJSDoc annotation.packages/shared/src/react/billing/useInitializePaymentMethod.tsx (4)
1-8: LGTM!Imports are appropriate and well-organized, using type-only imports where applicable.
10-17: LGTM!Type definitions are clear and properly exported for consumers. The result type correctly reflects both the cached data and the imperative trigger function.
22-52: LGTM!The hook implementation is well-structured:
- Proper resource selection based on
fortype- Memoized queryKey prevents unnecessary re-renders
- Defensive null check in queryFn is good practice
- Query options (staleTime, refetchOnWindowFocus, placeholderData) are appropriate for this use case
76-76: LGTM!The internal alias export is consistent with the pattern used across the billing hooks in this PR.
| "dependencies": { | ||
| "@tanstack/query-core": "5.87.4", | ||
| "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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
@tanstack/query-core latest version npm registry
💡 Result:
The latest published version on the npm registry is 5.91.1 (published Nov 25, 2025). [1][2]
Sources:
- Yarn package listing showing 5.91.1 (Nov 25, 2025). [1]
- Maven / package metadata (TanStack Query versions). [2]
🏁 Script executed:
# Check if there are other references to @tanstack/query-core in the monorepo
rg "@tanstack/query-core" --type json -A 2Repository: clerk/javascript
Length of output: 481
🏁 Script executed:
# Verify the packages/shared/package.json file structure and other dependencies
head -150 packages/shared/package.json | tail -50Repository: clerk/javascript
Length of output: 1618
Dependency promotion is correct, but version is outdated.
Moving @tanstack/query-core from devDependencies to dependencies is appropriate since React Query observers are now used at runtime through @clerk/shared. However, version 5.87.4 is behind the latest 5.91.1 (released Nov 25, 2025). Consider updating to the latest version for bug fixes and improvements, especially as a runtime dependency now exposed to consumers. Note that packages/clerk-js also pins 5.87.4, so any update should be coordinated across the monorepo.
🤖 Prompt for AI Agents
In packages/shared/package.json around lines 126 to 131, @tanstack/query-core
was promoted to dependencies but is pinned to an outdated 5.87.4; update the
dependency version to the latest 5.91.1, coordinate the same version bump in
packages/clerk-js to keep the monorepo consistent, run CI/tests and regenerate
the lockfile (npm/yarn/pnpm) to capture the updated resolution, and verify there
are no breaking API changes or type errors after the upgrade before merging.
| function useStripeLoader(options: StripeLoaderOptions): UseStripeLoaderResult { | ||
| const { stripeClerkLibs, externalGatewayId, stripePublishableKey } = options; | ||
|
|
||
| const queryKey = useMemo(() => { | ||
| return ['stripe-sdk', { externalGatewayId, stripePublishableKey }] as const; | ||
| }, [externalGatewayId, stripePublishableKey]); | ||
|
|
||
| const billingEnabled = useBillingHookEnabled({ authenticated: true }); | ||
|
|
||
| const isEnabled = Boolean(stripeClerkLibs && externalGatewayId && stripePublishableKey) && billingEnabled; | ||
|
|
||
| const query = useClerkQuery({ | ||
| queryKey, | ||
| queryFn: () => { | ||
| if (!stripeClerkLibs || !externalGatewayId || !stripePublishableKey) { | ||
| return null; | ||
| } | ||
|
|
||
| return stripeClerkLibs.loadStripe(stripePublishableKey, { | ||
| stripeAccount: externalGatewayId, | ||
| }); | ||
| }, | ||
| enabled: isEnabled, | ||
| staleTime: 1_000 * 60, | ||
| refetchOnWindowFocus: false, | ||
| placeholderData: defineKeepPreviousDataFn(true), | ||
| }); | ||
|
|
||
| return query.data; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t always keep previous Stripe instance across key/account changes.
Unconditional keepPreviousData here can transiently return the wrong Stripe object when inputs change.
const query = useClerkQuery({
queryKey,
queryFn: () => {
if (!stripeClerkLibs || !externalGatewayId || !stripePublishableKey) {
return null;
}
return stripeClerkLibs.loadStripe(stripePublishableKey, {
stripeAccount: externalGatewayId,
});
},
enabled: isEnabled,
staleTime: 1_000 * 60,
refetchOnWindowFocus: false,
- placeholderData: defineKeepPreviousDataFn(true),
+ placeholderData: defineKeepPreviousDataFn(isEnabled),
});🤖 Prompt for AI Agents
In packages/shared/src/react/billing/useStripeLoader.tsx around lines 20 to 49,
the query unconditionally keeps the previous Stripe instance via
placeholderData: defineKeepPreviousDataFn(true) which can return the wrong
Stripe object when the key/account changes; change this so previous data is not
kept across key/account changes by disabling keep-previous behavior (remove the
placeholderData or set it to defineKeepPreviousDataFn(false) / set
keepPreviousData: false) so the query returns null or the new instance
immediately when inputs change.
| const calls = getPlansSpy.mock.calls.map(call => call[0]?.for); | ||
|
|
||
| if (isRQ) { | ||
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); | ||
| expect(calls.every(value => value === 'user')).toBe(true); | ||
| } else { | ||
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBe(1)); | ||
| expect(getPlansSpy.mock.calls[0][0]).toEqual( | ||
| expect.objectContaining({ | ||
| for: 'user', | ||
| }), | ||
| ); | ||
| } | ||
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); | ||
| expect(calls.every(value => value === 'user')).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical test logic error: calls captured before async operations complete.
Line 264 captures calls from the mock spy, but line 266 waits for the spy to be called afterward. This means line 267 asserts on stale data captured before the revalidation calls were made, causing the test to validate incorrect state.
Apply this diff to capture calls after they complete:
- const calls = getPlansSpy.mock.calls.map(call => call[0]?.for);
-
await waitFor(() => expect(getPlansSpy.mock.calls.length).toBeGreaterThanOrEqual(1));
+
+ const calls = getPlansSpy.mock.calls.map(call => call[0]?.for);
expect(calls.every(value => value === 'user')).toBe(true);📝 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.
| const calls = getPlansSpy.mock.calls.map(call => call[0]?.for); | |
| if (isRQ) { | |
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); | |
| expect(calls.every(value => value === 'user')).toBe(true); | |
| } else { | |
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBe(1)); | |
| expect(getPlansSpy.mock.calls[0][0]).toEqual( | |
| expect.objectContaining({ | |
| for: 'user', | |
| }), | |
| ); | |
| } | |
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); | |
| expect(calls.every(value => value === 'user')).toBe(true); | |
| await waitFor(() => expect(getPlansSpy.mock.calls.length).toBeGreaterThanOrEqual(1)); | |
| const calls = getPlansSpy.mock.calls.map(call => call[0]?.for); | |
| expect(calls.every(value => value === 'user')).toBe(true); |
🤖 Prompt for AI Agents
In packages/shared/src/react/hooks/__tests__/usePlans.spec.tsx around lines 264
to 267, the test captures the mock calls into `calls` before awaiting the async
revalidation, so the assertion runs on stale data; move the line that maps
`getPlansSpy.mock.calls` to `calls` to after the await/waitFor that ensures the
spy has been called, then assert that every captured call has `for === 'user'`.
Ensure you re-read the mock.calls after the waitFor (not before) so the
assertion checks the completed async calls.
| const [paginatedPage, setPaginatedPage] = useState(config.initialPage ?? 1); | ||
|
|
||
| // Cache initialPage and initialPageSize until unmount | ||
| const initialPageRef = useRef(config.initialPage ?? 1); | ||
| const pageSizeRef = useRef(config.pageSize ?? 10); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix initialPage handling (page can become 0; infinite page and next/prev break when initialPage ≠ 1).
Current math will misreport page and can compute incorrect hasNextPage/hasPreviousPage for offset starts.
- const [paginatedPage, setPaginatedPage] = useState(config.initialPage ?? 1);
+ const initialPage = Math.max(1, config.initialPage ?? 1);
+ const [paginatedPage, setPaginatedPage] = useState(initialPage);
...
- const initialPageRef = useRef(config.initialPage ?? 1);
+ const initialPageRef = useRef(initialPage);
...
- page: validPages.length > 0 ? validPages.length : initialPageRef.current,
+ page:
+ validPages.length > 0 ? initialPageRef.current + validPages.length - 1 : initialPageRef.current,
};
}
...
- const fetchNext = useCallback(() => {
+ const fetchNext = useCallback(() => {
if (triggerInfinite) {
void infiniteQuery.fetchNextPage({ cancelRefetch: false });
return;
}
- setPaginatedPage(n => Math.max(0, n + 1));
+ setPaginatedPage(n => Math.max(initialPageRef.current, n + 1));
}, [infiniteQuery, triggerInfinite]);
const fetchPrevious = useCallback(() => {
if (triggerInfinite) {
// not natively supported by forward-only pagination; noop
return;
}
- setPaginatedPage(n => Math.max(0, n - 1));
+ setPaginatedPage(n => Math.max(initialPageRef.current, n - 1));
}, [triggerInfinite]);
const offsetCount = (initialPageRef.current - 1) * pageSizeRef.current;
- const pageCount = Math.ceil((count - offsetCount) / pageSizeRef.current);
+ const pageCount = Math.ceil(Math.max(0, count - offsetCount) / pageSizeRef.current);
const hasNextPage = triggerInfinite
? Boolean(infiniteQuery.hasNextPage)
- : count - offsetCount * pageSizeRef.current > page * pageSizeRef.current;
+ : count > page * pageSizeRef.current;
const hasPreviousPage = triggerInfinite
? Boolean(infiniteQuery.hasPreviousPage)
- : (page - 1) * pageSizeRef.current > offsetCount * pageSizeRef.current;
+ : page > initialPageRef.current;Also applies to: 114-133, 185-209
| const infiniteQuery = useClerkInfiniteQuery<ClerkPaginatedResponse<any>, any, any, typeof infiniteQueryKey, any>({ | ||
| queryKey: infiniteQueryKey, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tighten exported typing (remove any casts on revalidate / setData, and reduce any generics).
Since usePagesOrInfinite is exported, the as any surface area will leak and defeat type-safety guarantees.
Also applies to: 232-255
🤖 Prompt for AI Agents
In packages/shared/src/react/hooks/usePagesOrInfinite.tsx around lines 82-83
(and similarly lines 232-255), the call to useClerkInfiniteQuery and the hook’s
exported API use broad any generics and cast revalidate/setData to any; tighten
the exported typing by introducing proper generic type parameters for the hook
(e.g., Item and Page/Response types that map to ClerkPaginatedResponse<Item>),
use those types as the generics for useClerkInfiniteQuery instead of any, and
remove the as any casts on revalidate and setData by giving them the correct
typed signatures (e.g., revalidate: () =>
Promise<void|ClerkPaginatedResponse<Item>> or the exact return type, and
setData: (updater: UpdaterFunction<Page[]|undefined>) => void) so the hook’s
return type is fully typed and no any leaks occur. Ensure to propagate these
types through all internal calls and the exported return type so the hook
remains type-safe for consumers.
| return { | ||
| data: query.data, | ||
| error: (query.error ?? null) as StatementQueryResult['error'], | ||
| isLoading: query.isLoading, | ||
| isFetching: query.isFetching, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the useStatementQuery.tsx file to understand context
head -70 packages/shared/src/react/hooks/useStatementQuery.tsx | tail -30Repository: clerk/javascript
Length of output: 878
🏁 Script executed:
# Find the useClerkQuery hook definition and its error type
rg -n "useClerkQuery" packages/shared/src/react -A 5 -B 2 | head -50Repository: clerk/javascript
Length of output: 4435
🏁 Script executed:
# Find StatementQueryResult type definition
rg -n "StatementQueryResult" packages/shared/src/react -B 3 -A 5 | head -60Repository: clerk/javascript
Length of output: 2965
🏁 Script executed:
# Search for ClerkAPIResponseError usage and definition
rg -n "ClerkAPIResponseError" packages/shared/src -B 2 -A 2 | head -50Repository: clerk/javascript
Length of output: 3806
🏁 Script executed:
# Read the complete StatementQueryResult type definition
cat -n packages/shared/src/react/hooks/useStatementQuery.types.ts | sed -n '34,50p'Repository: clerk/javascript
Length of output: 765
🏁 Script executed:
# Find the useClerkQuery definition and its return type
rg -n "function useClerkQuery|export.*useClerkQuery" packages/shared/src/react/clerk-rq -A 30 -B 2 | head -80Repository: clerk/javascript
Length of output: 3129
🏁 Script executed:
# Check the types for useClerkQuery more specifically
rg -n "useClerkQuery" packages/shared/src/react/clerk-rq -lRepository: clerk/javascript
Length of output: 218
🏁 Script executed:
# Find DefaultError definition from React Query
rg -n "DefaultError" packages/shared/src/react/clerk-rq -B 2 -A 2Repository: clerk/javascript
Length of output: 12019
🏁 Script executed:
# Check CommonQueryResult type definition
rg -n "CommonQueryResult" packages/shared/src/react/clerk-rq -B 2 -A 5Repository: clerk/javascript
Length of output: 4486
🏁 Script executed:
# Check useBaseQuery to understand error handling
rg -n "useBaseQuery" packages/shared/src/react/clerk-rq/useBaseQuery.ts -A 40 | head -60Repository: clerk/javascript
Length of output: 2744
🏁 Script executed:
# Search for clerk.billing implementation
rg -n "billing\.getStatement" packages/shared/src -A 5 -B 2 | head -50Repository: clerk/javascript
Length of output: 1402
🏁 Script executed:
# Check if there's error handling wrapper around Clerk API calls
rg -n "getStatement" packages/shared/src -B 3 -A 10 | head -80Repository: clerk/javascript
Length of output: 4192
🏁 Script executed:
# Look for any error mappers or transformers in useClerkQuery or related files
rg -n "error.*transform|mapError|onError" packages/shared/src/react/clerk-rq -B 2 -A 3Repository: clerk/javascript
Length of output: 42
Pass explicit error type to useClerkQuery or add type narrowing for the error.
The type assertion on line 53 assumes query.error is typed as ClerkAPIResponseError, but useClerkQuery is called without an explicit TError parameter, defaulting to React Query's DefaultError. This creates a type mismatch:
clerk.billing.getStatement()is expected to throwClerkAPIResponseError(based on Clerk SDK patterns)- The manual error throw at line 42 is a plain
Error, notClerkAPIResponseError - The assertion hides this type safety issue
Either:
- Pass explicit type to useClerkQuery:
useClerkQuery<BillingStatementResource, ClerkAPIResponseError>({ ... }) - Or add a type guard:
error: isClerkAPIResponseError(query.error) ? query.error : null
| /** | ||
| * @internal | ||
| */ | ||
| export function useSubscription(params?: UseSubscriptionParams): SubscriptionResult { | ||
| useAssertWrappedByClerkProvider(HOOK_NAME); | ||
|
|
||
| const clerk = useClerkInstanceContext(); | ||
| const user = useUserContext(); | ||
| const { organization } = useOrganizationContext(); | ||
|
|
||
| const billingEnabled = useBillingHookEnabled(params); | ||
|
|
||
| clerk.telemetry?.record(eventMethodCalled(HOOK_NAME)); | ||
|
|
||
| const keepPreviousData = params?.keepPreviousData ?? false; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move telemetry recording out of render (avoid duplicate events per re-render).
Right now record() fires on every render, not just on mount.
-import { useCallback } from 'react';
+import { useCallback, useEffect } from 'react';
...
- clerk.telemetry?.record(eventMethodCalled(HOOK_NAME));
+ useEffect(() => {
+ clerk.telemetry?.record(eventMethodCalled(HOOK_NAME));
+ }, [clerk]);🤖 Prompt for AI Agents
In packages/shared/src/react/hooks/useSubscription.tsx around lines 20-35, the
clerk.telemetry?.record(eventMethodCalled(HOOK_NAME)) call runs on every render;
move it out of the render path by executing it inside a useEffect and ensure it
only fires once: create a local ref (e.g., recordedRef) to guard that the
telemetry call runs once, then in useEffect with clerk in deps check if
recordedRef.current is false and clerk?.telemetry exists, call record and set
recordedRef.current = true; this prevents duplicate events while still ensuring
the event is recorded when clerk becomes available.
| /** | ||
| * @internal | ||
| */ | ||
| export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: any }>) { | ||
| return <>{children}</>; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add comprehensive JSDoc for the compatibility shim.
The component lacks documentation explaining its purpose as a compatibility layer during SWR removal. As per coding guidelines, public APIs (even those marked @internal) should include comprehensive JSDoc with @param and @returns tags.
Apply this diff to add detailed documentation:
/**
+ * Compatibility wrapper that replaces SWRConfig after SWR removal.
+ * This component maintains the same interface for backward compatibility
+ * but no longer applies any configuration, simply rendering children directly.
+ *
* @internal
+ * @param props - Component props
+ * @param props.children - Child elements to render
+ * @param props.swrConfig - Ignored for compatibility (SWR has been removed)
+ * @returns The children elements without any wrapper logic
*/
export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: unknown }>) {
return <>{children}</>;
}📝 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.
| /** | |
| * @internal | |
| */ | |
| export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: any }>) { | |
| return <>{children}</>; | |
| } | |
| /** | |
| * Compatibility wrapper that replaces SWRConfig after SWR removal. | |
| * This component maintains the same interface for backward compatibility | |
| * but no longer applies any configuration, simply rendering children directly. | |
| * | |
| * @internal | |
| * @param props - Component props | |
| * @param props.children - Child elements to render | |
| * @param props.swrConfig - Ignored for compatibility (SWR has been removed) | |
| * @returns The children elements without any wrapper logic | |
| */ | |
| export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: unknown }>) { | |
| return <>{children}</>; | |
| } |
🤖 Prompt for AI Agents
In packages/shared/src/react/providers/SWRConfigCompat.tsx around lines 3 to 8,
add a comprehensive JSDoc block above the SWRConfigCompat function: document
that this component is a temporary compatibility shim used while SWR is being
removed, mark it @internal, describe the props (children: React nodes and
optional swrConfig: any) with @param tags, state that it simply renders children
and does not modify behavior, and include an @returns tag describing that it
returns the children React element; keep the wording concise and consistent with
project JSDoc style.
| /** | ||
| * @internal | ||
| */ | ||
| export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: any }>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace any with unknown for unused prop.
The swrConfig prop is typed as any, which violates the coding guideline to avoid any types. Since this prop is intentionally unused (compatibility shim), it should be typed as unknown instead.
As per coding guidelines, prefer unknown over any when the type is uncertain or unused.
Apply this diff:
-export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: any }>) {
+export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: unknown }>) {📝 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.
| export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: any }>) { | |
| export function SWRConfigCompat({ children }: PropsWithChildren<{ swrConfig?: unknown }>) { |
🤖 Prompt for AI Agents
In packages/shared/src/react/providers/SWRConfigCompat.tsx around line 6, the
swrConfig prop is declared as `any`; change its type to `unknown` since the prop
is intentionally unused — update the component signature to use
PropsWithChildren<{ swrConfig?: unknown }> so the code follows the guideline to
avoid `any`.
panteliselef
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you ❣️
|
We've found a few last outstanding bugs in the RQ implementation so I think we should hold this until we've fixed those. I've pinged you elsewhere with details @jacekradko. 😄 |
Description
Fixes: USER-4034
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit
Refactor
Chores
Tests
✏️ Tip: You can customize this high-level summary in your review settings.