- 
                Notifications
    
You must be signed in to change notification settings  - Fork 402
 
          [WIP] feat(backend, clerk-js): Update the supported API version to 2025-10-01
          #7095
        
          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?
  
    [WIP] feat(backend, clerk-js): Update the supported API version to 2025-10-01
  
  #7095
              Conversation
          🦋 Changeset detectedLatest commit: ed3a125 The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 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. 
  | 
    
          
WalkthroughBumped frontend/backend supported API versions; made BillingPlan annual fee fields nullable across types and JSON; updated model deserialization (adds a debug log); updated many UI components and tests to handle optional annual pricing; renamed a checkout param from  Changes
 Sequence Diagram(s)sequenceDiagram
  participant UI as UI component
  participant Model as BillingPlan.fromJSON
  participant Types as Types
  participant Checkout as Checkout flow
  UI->>Model: request plan JSON
  Note right of Model: annual_fee / annual_monthly_fee may be null\n(fromJSON logs values)
  Model-->>UI: BillingPlan (annualMonthlyFee may be null)
  alt annualMonthlyFee present
    UI->>UI: show annual toggle
    UI->>Checkout: use plan.annualMonthlyFee for annual pricing
  else annualMonthlyFee absent
    UI->>UI: hide annual toggle
    UI->>Checkout: fallback to plan.fee
  end
  UI->>Checkout: submit confirm checkout (paymentMethodId)
  Checkout-->>Types: ConfirmCheckoutParams includes paymentMethodId
    Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 
 Poem
 Pre-merge checks and finishing touches❌ Failed checks (1 warning)
 ✅ Passed checks (2 passed)
 ✨ Finishing touches
 🧪 Generate unit tests (beta)
 Comment   | 
    
2025-10-012025-10-01
      2025-10-012025-10-01
      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: 1
📜 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 selected for processing (2)
packages/backend/src/constants.ts(1 hunks)packages/clerk-js/src/core/constants.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.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
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor 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
Useconst assertionsfor literal types:as const
Usesatisfiesoperator 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 ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
🔇 Additional comments (1)
packages/clerk-js/src/core/constants.ts (1)
57-57: Version update is properly integrated and tested.The verification confirms that
SUPPORTED_FAPI_VERSION = '2025-10-01'is correctly implemented:
- The version is consistently passed as
 __clerk_api_versionquery parameter in all API requests viafapiClient.buildUrl()- Comprehensive test coverage (15+ test cases in
 fapiClient.test.ts) confirms the version is correctly appended to request URLs- Type definitions in
 @clerk/typesare generically designed to handle versioned API responses without requiring version-specific schema switchingThe frontend does not handle version-dependent response schema changes—this responsibility belongs to the backend, which validates and returns appropriate responses based on the
__clerk_api_versionparameter. The generic type system ensures compatibility across API versions.
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: 1
📜 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 selected for processing (1)
.changeset/hot-jars-smell.md(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/**
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Automated releases must use Changesets.
Files:
.changeset/hot-jars-smell.md
⏰ 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). (2)
- GitHub Check: semgrep-cloud-platform/scan
 - GitHub Check: semgrep-cloud-platform/scan
 
          
@clerk/agent-toolkit
 @clerk/astro
 @clerk/backend
 @clerk/chrome-extension
 @clerk/clerk-js
 @clerk/dev-cli
 @clerk/elements
 @clerk/clerk-expo
 @clerk/expo-passkeys
 @clerk/express
 @clerk/fastify
 @clerk/localizations
 @clerk/nextjs
 @clerk/nuxt
 @clerk/clerk-react
 @clerk/react-router
 @clerk/remix
 @clerk/shared
 @clerk/tanstack-react-start
 @clerk/testing
 @clerk/themes
 @clerk/types
 @clerk/upgrade
 @clerk/vue
 commit:   | 
    
2025-10-012025-10-01
      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: 8
🧹 Nitpick comments (1)
packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx (1)
159-163: Consider adding non-null assertion for type safety.The
planFeecalculation at line 162 may cause a TypeScript error becauseplan.annualMonthlyFeeis inferred asBillingMoneyAmount | null, even though the logic ensures it's non-null when reached.Consider updating the code to match the pattern in PricingTableDefault.tsx:
- const planFee = !plan.annualMonthlyFee - ? plan.fee - : planPeriod === 'annual' - ? plan.annualMonthlyFee - : plan.fee; + const planFee = !plan.annualMonthlyFee + ? plan.fee + : planPeriod === 'annual' + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.annualMonthlyFee! + : plan.fee;
📜 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 selected for processing (13)
packages/backend/src/tokens/__tests__/handshake.test.ts(1 hunks)packages/clerk-js/src/core/resources/BillingPlan.ts(2 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(1 hunks)packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx(1 hunks)packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx(2 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx(1 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx(3 hunks)packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(3 hunks)packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx(1 hunks)packages/clerk-js/src/ui/contexts/components/Plans.tsx(2 hunks)packages/types/src/billing.ts(1 hunks)packages/types/src/json.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/backend/src/tokens/tests/handshake.test.ts
 
🧰 Additional context used
📓 Path-based instructions (9)
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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{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
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor 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
Useconst assertionsfor literal types:as const
Usesatisfiesoperator 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 ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor 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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.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/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
🧬 Code graph analysis (2)
packages/clerk-js/src/core/resources/BillingPlan.ts (2)
packages/types/src/billing.ts (1)
BillingMoneyAmount(615-632)packages/clerk-js/src/utils/billing.ts (1)
billingMoneyAmountFromJSON(10-17)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
normalizeFormatted(27-29)
⏰ 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: Analyze (javascript-typescript)
 - GitHub Check: semgrep-cloud-platform/scan
 
🔇 Additional comments (12)
packages/clerk-js/src/core/resources/BillingPlan.ts (2)
11-12: LGTM: Nullable field declarations are correct.Making
annualFeeandannualMonthlyFeenullable with defaultnullproperly reflects the API changes and allows plans without annual pricing options.
44-45: LGTM: Proper null-safe JSON deserialization.The ternary operator correctly handles nullable annual pricing fields, setting them to
nullwhen absent from the API response.packages/types/src/json.ts (1)
637-638: LGTM: Type definitions correctly reflect nullable annual pricing.Making
annual_feeandannual_monthly_feenullable in the JSON type definitions properly aligns with the API version 2025-10-01 changes.packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts (1)
37-37: LGTM: Presence-based check is safer.Switching from
plan.annualMonthlyFee.amount > 0toBoolean(plan.annualMonthlyFee)is the correct approach for nullable fields. This avoids potential null reference errors and properly handles the updated data model.packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
377-378: LGTM: Safe presence check for switchability.Using
Boolean(subscription.plan.annualMonthlyFee)is the correct approach to check if annual pricing is available before allowing period switching.packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx (2)
229-234: LGTM: Proper null handling with fallback.This code correctly checks for
annualMonthlyFeeexistence before using it, falling back toplan.feewhen it's null. This is the safe pattern that should be used throughout the codebase.
336-364: LGTM: Conditional rendering based on existence.Using
plan.annualMonthlyFeein a conditional expression properly handles nullable fields. The toggle is only shown when annual pricing is available, with an appropriate notice displayed otherwise.packages/types/src/billing.ts (1)
134-140: LGTM! Type definitions accurately reflect nullable annual pricing.The type changes for
annualFeeandannualMonthlyFeeto include| nullare well-documented and align with the broader API version update. Since the interface is marked as@experimental, this change is appropriate.packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (1)
287-296: LGTM! Non-null assertion is safe in this context.The fee calculation correctly handles nullable
annualMonthlyFee. The non-null assertion on line 294 is safe becauseplanSupportsAnnualis onlytruewhenannualMonthlyFeeis truthy.packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx (2)
63-63: Consistent with presence-based logic across the PR.The change to
Boolean(plan.annualMonthlyFee)aligns with the nullable type updates. This enables billing cycle controls whenannualMonthlyFeeexists, regardless of amount.
255-297: LGTM! Conditional rendering correctly handles nullable field.The truthiness check
plan.annualMonthlyFee ? (...)appropriately renders the annual billing notice only when annual pricing exists.packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
216-216: LGTM! Correctly checks for annual pricing eligibility.The use of
Boolean(plan?.annualMonthlyFee)with optional chaining safely checks whether a plan supports annual billing.
| console.log('data', { | ||
| fee: data.fee, | ||
| annual_fee: data.annual_fee, | ||
| annual_monthly_fee: data.annual_monthly_fee, | ||
| }); | 
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.
Remove debug console.log before merging.
Debug logging should not be committed to production code. This will clutter browser consoles and could expose internal data structures.
Apply this diff to remove the debug statement:
-    console.log('data', {
-      fee: data.fee,
-      annual_fee: data.annual_fee,
-      annual_monthly_fee: data.annual_monthly_fee,
-    });
-
     this.id = data.id;📝 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.
| console.log('data', { | |
| fee: data.fee, | |
| annual_fee: data.annual_fee, | |
| annual_monthly_fee: data.annual_monthly_fee, | |
| }); | |
| this.id = data.id; | 
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/BillingPlan.ts around lines 35 to 39,
there is a debug console.log printing the fee/annual_fee/annual_monthly_fee
object; remove this debug statement before merging (delete the console.log call
and its object) or replace it with a non-debugging, environment-gated logging
mechanism if persistent logging is required, ensuring no sensitive/internal
structures are emitted to the browser console.
| const fee = | ||
| planPeriod === 'month' | ||
| ? plan.fee | ||
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| plan.annualMonthlyFee!; | 
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.
Unsafe non-null assertion could cause runtime errors.
Same issue as in PaymentAttemptPage: using plan.annualMonthlyFee! without verification could throw if the field is null.
Apply this safer pattern:
-  const fee =
-    planPeriod === 'month'
-      ? plan.fee
-      : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        plan.annualMonthlyFee!;
+  const fee =
+    planPeriod === 'month' || !plan.annualMonthlyFee
+      ? plan.fee
+      : plan.annualMonthlyFee;📝 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 fee = | |
| planPeriod === 'month' | |
| ? plan.fee | |
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| plan.annualMonthlyFee!; | |
| const fee = | |
| planPeriod === 'month' || !plan.annualMonthlyFee | |
| ? plan.fee | |
| : plan.annualMonthlyFee; | 
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx around lines 42
to 46, the code uses an unsafe non-null assertion plan.annualMonthlyFee! which
can throw at runtime if that field is null; replace the assertion with a guarded
access and explicit handling: check whether plan.annualMonthlyFee is defined
before using it (e.g., use a conditional/fallback value or throw a descriptive
error), or compute fee via a nullish coalescing/fallback expression so fee is
never assigned an unchecked null value; ensure TypeScript types reflect the
runtime check so there are no non-null assertions left.
| const fee = | ||
| subscriptionItem.planPeriod === 'month' ? subscriptionItem.plan.fee : subscriptionItem.plan.annualMonthlyFee; | ||
| subscriptionItem.planPeriod === 'month' | ||
| ? subscriptionItem.plan.fee | ||
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| subscriptionItem.plan.annualMonthlyFee!; | 
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.
Unsafe non-null assertion could cause runtime errors.
The code uses a non-null assertion (plan.annualMonthlyFee!) without first verifying that annualMonthlyFee exists. Since this field is now nullable (as per the type changes), this could throw a runtime error if the API returns null.
Apply this defensive pattern instead:
-  const fee =
-    subscriptionItem.planPeriod === 'month'
-      ? subscriptionItem.plan.fee
-      : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        subscriptionItem.plan.annualMonthlyFee!;
+  const fee =
+    subscriptionItem.planPeriod === 'month' || !subscriptionItem.plan.annualMonthlyFee
+      ? subscriptionItem.plan.fee
+      : subscriptionItem.plan.annualMonthlyFee;📝 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 fee = | |
| subscriptionItem.planPeriod === 'month' ? subscriptionItem.plan.fee : subscriptionItem.plan.annualMonthlyFee; | |
| subscriptionItem.planPeriod === 'month' | |
| ? subscriptionItem.plan.fee | |
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| subscriptionItem.plan.annualMonthlyFee!; | |
| const fee = | |
| subscriptionItem.planPeriod === 'month' || !subscriptionItem.plan.annualMonthlyFee | |
| ? subscriptionItem.plan.fee | |
| : subscriptionItem.plan.annualMonthlyFee; | 
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx
around lines 217-221, the code uses a non-null assertion on
subscriptionItem.plan.annualMonthlyFee which is now nullable; replace the unsafe
assertion with a defensive check: if annualMonthlyFee is present use it,
otherwise fall back to a safe default (e.g., plan.fee, 0, or a user-friendly
placeholder) or throw/log a clear error before using the value so no runtime
null access occurs. Ensure the code handles both month and non-month periods
without the `!` operator and preserves type-safety.
| const { name, annualMonthlyFee } = plan; | ||
| 
               | 
          ||
| const planSupportsAnnual = annualMonthlyFee.amount > 0; | ||
| const planSupportsAnnual = Boolean(annualMonthlyFee); | 
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
Verify behavior change: zero-amount annual pricing.
The logic changed from checking annualMonthlyFee.amount > 0 to Boolean(annualMonthlyFee). This means plans with an annual option but zero cost (amount: 0) will now show the annual toggle, whereas previously they would not.
Run the following script to check if any plans in the codebase have annual pricing with zero amounts:
🏁 Script executed:
#!/bin/bash
# Search for test data or fixtures with annual pricing
rg -n -A5 -B5 'annualMonthlyFee|annual_monthly_fee' --type=ts --type=json --type=tsxLength of output: 27
I'll retry with corrected ripgrep syntax and examine the actual file:
🏁 Script executed:
#!/bin/bash
# Search for annualMonthlyFee without specifying tsx type
rg -n -A5 -B5 'annualMonthlyFee|annual_monthly_fee'Length of output: 74162
🏁 Script executed:
#!/bin/bash
# Also examine the actual file at line 285 with context
cat packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx | head -300 | tail -50Length of output: 1579
🏁 Script executed:
#!/bin/bash
# Search for BillingMoneyAmount type definitions and mock data
rg -n 'BillingMoneyAmount|billingMoneyAmount' -g '*.ts' -g '*.tsx' -g '*.json'Length of output: 7912
🏁 Script executed:
#!/bin/bash
# Look for test files or fixtures related to pricing
fd -e test.ts -e test.tsx -e mock.ts -e mock.tsx -e fixture.ts | xargs grep -l 'annualMonthlyFee\|annual' 2>/dev/null || echo "No test files found with pricing patterns"Length of output: 585
🏁 Script executed:
#!/bin/bash
# Search for where pricing plans are created/mocked in the codebase
rg -n 'amount.*0|cost.*0|price.*0' packages/clerk-js/src/ui/components/PricingTable/ --type=tsLength of output: 1428
🏁 Script executed:
#!/bin/bash
# Look at the imports and structure of the PricingTableDefault file
head -50 packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxLength of output: 1542
Fix logic to check annualMonthlyFee?.amount > 0 instead of just Boolean(annualMonthlyFee).
Line 285 changed to check for object existence instead of positive amount. This breaks plans with zero-cost annual pricing—they will now show the annual toggle when they shouldn't. The test in pricing-footer-state.spec.ts line 108 ("does not show footer when switching period if annualMonthlyFee is 0") confirms the intended behavior: plans with annualMonthlyFee: { amount: 0, ... } must not be treated as supporting annual periods.
const planSupportsAnnual = Boolean(annualMonthlyFee?.amount > 0);
Note: This pattern also appears in Plans.tsx (lines 216, 329), pricing-footer-state.ts (line 37), and PricingTableMatrix.tsx (lines 63, 159)—verify whether those require similar fixes.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
around line 285 the logic was changed to Boolean(annualMonthlyFee) which treats
an object with amount: 0 as truthy; update the check to explicitly test that
annualMonthlyFee?.amount > 0 so only positive annual prices enable the annual
toggle; also search and update the same pattern in Plans.tsx (around lines 216,
329), pricing-footer-state.ts (around line 37), and PricingTableMatrix.tsx
(around lines 63, 159) to use annualMonthlyFee?.amount > 0 where appropriate to
preserve the test behavior that zero-cost annual plans do not support annual
periods.
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted), | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| currency: subscription.plan.annualFee!.currencySymbol, | 
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.
Unsafe non-null assertion in localization keys.
The non-null assertions for subscription.plan.annualFee! are unsafe. If annualFee is null, this will throw at runtime when constructing the localization string.
Since this code path only executes when isSwitchable is true (which checks for annualMonthlyFee), you should also verify annualFee exists:
+      // Ensure annualFee exists before using it
+      if (!subscription.plan.annualFee) {
+        return null;
+      }
       isSwitchable
         ? {
             label:
               subscription.planPeriod === 'month'
                 ? localizationKeys('billing.switchToAnnualWithAnnualPrice', {
-                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-                    price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted),
-                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-                    currency: subscription.plan.annualFee!.currencySymbol,
+                    price: normalizeFormatted(subscription.plan.annualFee.amountFormatted),
+                    currency: subscription.plan.annualFee.currencySymbol,
                   })Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx around
lines 412 to 415, the code uses non-null assertions on
subscription.plan.annualFee which can throw if annualFee is null; add a guard
that verifies subscription.plan.annualFee exists before accessing
amountFormatted and currencySymbol (e.g., if (!subscription.plan.annualFee)
return/skip building the localized string or provide safe fallbacks), and
replace the non-null assertions with safe access or validated locals so the
localization string is only built when annualFee is present.
| const fee = | ||
| subscription.planPeriod === 'month' | ||
| ? subscription.plan.fee | ||
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| subscription.plan.annualFee!; | 
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.
Unsafe non-null assertion in fee calculation.
Using subscription.plan.annualFee! without verification could throw if the field is null.
Apply this safer pattern:
-  const fee =
-    subscription.planPeriod === 'month'
-      ? subscription.plan.fee
-      : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        subscription.plan.annualFee!;
+  const fee =
+    subscription.planPeriod === 'month' || !subscription.plan.annualFee
+      ? subscription.plan.fee
+      : subscription.plan.annualFee;🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx around
lines 485 to 489, the code uses a non-null assertion
subscription.plan.annualFee! which can throw if annualFee is null/undefined;
replace the assertion with a safe check — use optional chaining and a fallback
(e.g., subscription.plan.annualFee ?? 0 or another sensible default), or
explicitly handle the missing value (throw a descriptive error or return early).
Ensure fee is computed like: if planPeriod === 'month' use
subscription.plan.fee, otherwise use subscription.plan.annualFee ?? <fallback>
so no non-null assertion is needed.
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| const fee = subscription.planPeriod === 'annual' ? subscription.plan.annualFee! : subscription.plan.fee; | 
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.
Unsafe non-null assertion for annualFee.
Using subscription.plan.annualFee! without checking if it exists first could cause runtime errors when the field is null.
Apply this safer approach:
-  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-  const fee = subscription.planPeriod === 'annual' ? subscription.plan.annualFee! : subscription.plan.fee;
+  const fee = subscription.planPeriod === 'annual' && subscription.plan.annualFee 
+    ? subscription.plan.annualFee 
+    : subscription.plan.fee;📝 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.
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| const fee = subscription.planPeriod === 'annual' ? subscription.plan.annualFee! : subscription.plan.fee; | |
| const fee = subscription.planPeriod === 'annual' && subscription.plan.annualFee | |
| ? subscription.plan.annualFee | |
| : subscription.plan.fee; | 
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx
around lines 156-157, the code uses a non-null assertion
subscription.plan.annualFee! which can throw if annualFee is null; change this
to safely handle absence by first checking if subscription.plan.annualFee is
defined (e.g., use optional chaining and a fallback or conditional): when
planPeriod is 'annual' use subscription.plan.annualFee ?? fallbackValue (or
handle the null case explicitly), otherwise use subscription.plan.fee; ensure
TypeScript types reflect the fallback or null-check so no non-null assertion is
used.
6ac7b4d    to
    52a2409      
    Compare
  
    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: 6
♻️ Duplicate comments (4)
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx (1)
42-46: Unsafe non-null assertion could cause runtime errors.The non-null assertion
plan.annualMonthlyFee!on line 46 is unsafe. SinceannualMonthlyFeeis now nullable (as seen in the type updates inpackages/types/src/billing.ts), this will throw a runtime error if the field isnull.Apply the safer pattern suggested in the previous review:
- const fee = - planPeriod === 'month' - ? plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - plan.annualMonthlyFee!; + const fee = + planPeriod === 'month' || !plan.annualMonthlyFee + ? plan.fee + : plan.annualMonthlyFee;packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (2)
412-415: Unsafe non-null assertion in localization keys.The non-null assertions for
subscription.plan.annualFee!on lines 413 and 415 are unsafe. IfannualFeeisnull, this will throw at runtime.As suggested in the previous review, add a guard before this code path:
+ // Ensure annualFee exists before using it + if (subscription.planPeriod === 'month' && !subscription.plan.annualFee) { + return []; + } isSwitchable ? { label: subscription.planPeriod === 'month' ? localizationKeys('billing.switchToAnnualWithAnnualPrice', { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - currency: subscription.plan.annualFee!.currencySymbol, + price: normalizeFormatted(subscription.plan.annualFee.amountFormatted), + currency: subscription.plan.annualFee.currencySymbol, })
485-489: Unsafe non-null assertion in fee calculation.Using
subscription.plan.annualFee!without verification on line 489 could throw if the field isnull.Apply this safer pattern:
- const fee = - subscription.planPeriod === 'month' - ? subscription.plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - subscription.plan.annualFee!; + const fee = + subscription.planPeriod === 'month' || !subscription.plan.annualFee + ? subscription.plan.fee + : subscription.plan.annualFee;packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (1)
285-285: Critical: Incorrect logic allows zero-cost annual plans to show annual toggle.Line 285 changed to check
Boolean(annualMonthlyFee)instead ofannualMonthlyFee.amount > 0. This breaks the intended behavior: plans with annual pricing set to 0 ({amount: 0, ...}) will now be treated as supporting annual periods when they shouldn't.The test in
pricing-footer-state.spec.tsline 108 explicitly expects: "does not show footer when switching period if annualMonthlyFee is 0".- const planSupportsAnnual = Boolean(annualMonthlyFee); + const planSupportsAnnual = Boolean(annualMonthlyFee && annualMonthlyFee.amount > 0);Note: This same pattern also appears in:
Plans.tsx(lines 216, 329)pricing-footer-state.ts(line 37)PricingTableMatrix.tsx(lines 63, 159)Verify and update those locations as well.
Based on learnings
📜 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 selected for processing (15)
packages/clerk-js/src/core/resources/BillingPlan.ts(2 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(1 hunks)packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx(1 hunks)packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx(2 hunks)packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx(2 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx(1 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx(3 hunks)packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(3 hunks)packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx(1 hunks)packages/clerk-js/src/ui/contexts/components/Plans.tsx(2 hunks)packages/clerk-js/src/utils/billing.ts(1 hunks)packages/types/src/billing.ts(1 hunks)packages/types/src/json.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- packages/clerk-js/src/ui/contexts/components/Plans.tsx
 - packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx
 - packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx
 - packages/types/src/json.ts
 - packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
 - packages/clerk-js/src/core/resources/BillingPlan.ts
 - packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts
 
🧰 Additional context used
📓 Path-based instructions (13)
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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{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
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor 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
Useconst assertionsfor literal types:as const
Usesatisfiesoperator 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 ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor 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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
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/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
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/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.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/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.test.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.test.{jsx,tsx}: Use React Testing Library
Test component behavior, not implementation
Use proper test queries
Implement proper test isolation
Use proper test coverage
Test component interactions
Use proper test data
Implement proper test setup
Use proper test cleanup
Implement proper test assertions
Use proper test structure
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
**/__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/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
🧬 Code graph analysis (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
normalizeFormatted(27-29)
⏰ 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: Analyze (javascript-typescript)
 - GitHub Check: semgrep-cloud-platform/scan
 - GitHub Check: semgrep-cloud-platform/scan
 
🔇 Additional comments (3)
packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx (1)
265-266: LGTM! Test data correctly reflects nullable annual pricing.The change to null values for
annualFeeandannualMonthlyFeein the Free Plan scenario correctly aligns with the type updates that made these fields nullable. This ensures tests cover the case where annual pricing is not available.packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx (1)
224-225: LGTM! Test data correctly reflects nullable annual pricing.The changes to null values for
annualFeeandannualMonthlyFeeproperly test scenarios where annual pricing is unavailable. This aligns with the type updates inpackages/types/src/billing.tsthat made these fields nullable.Also applies to: 258-259
packages/clerk-js/src/utils/billing.ts (1)
28-36: The original review comment is based on incorrect reasoning about JavaScript truthiness.The truthy checks on lines 28, 31, and 34 will not exclude zero-amount values. Since
BillingMoneyAmountJSONis an object (e.g.,{amount: 0, amountFormatted: '0.00', ...}), and objects are truthy in JavaScript, these conditions will evaluate to true and the assignments will occur. The type definitions confirm these fields are required (not nullable) inBillingCheckoutTotalsJSONandBillingStatementTotalsJSON, and extensive test coverage demonstrates zero-amount objects are expected in the output.The distinction between the original
incheck and the new truthy check only matters if the value isnull,undefined, or a falsy scalar—not for objects with zero-amount properties inside them.Likely an incorrect or invalid review comment.
| return planPeriod === 'annual' | ||
| ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| plan.annualMonthlyFee! | 
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.
Unsafe non-null assertion when annual pricing is enabled.
Line 294 uses plan.annualMonthlyFee! with a non-null assertion. If planSupportsAnnual is true but annualMonthlyFee is somehow null (e.g., data inconsistency), this will throw at runtime.
Consider a safer pattern:
-    return planPeriod === 'annual'
-      ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        plan.annualMonthlyFee!
-      : plan.fee;
+    if (planPeriod === 'annual' && plan.annualMonthlyFee) {
+      return plan.annualMonthlyFee;
+    }
+    return plan.fee;🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
around lines 292 to 294, the code uses an unsafe non-null assertion
plan.annualMonthlyFee! when planPeriod === 'annual'; instead, guard against a
missing annualMonthlyFee by checking for its presence (or using a nullish
fallback) before returning it — for example, return the annualPrice if defined,
otherwise fall back to a sensible default (like plan.monthlyFee or throw/log a
descriptive error) so the UI doesn't crash on inconsistent data.
| const gridTemplateColumns = React.useMemo(() => `repeat(${plans.length + 1}, minmax(9.375rem,1fr))`, [plans.length]); | ||
| 
               | 
          ||
| const renderBillingCycleControls = React.useMemo(() => plans.some(plan => plan.annualMonthlyFee.amount > 0), [plans]); | ||
| const renderBillingCycleControls = React.useMemo(() => plans.some(plan => Boolean(plan.annualMonthlyFee)), [plans]); | 
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: Incorrect logic allows zero-cost annual plans to show billing controls.
Line 63 changed to check Boolean(plan.annualMonthlyFee) instead of verifying annualMonthlyFee.amount > 0. This means plans with zero-cost annual pricing ({amount: 0, ...}) will incorrectly display annual billing controls.
This is inconsistent with test expectations in pricing-footer-state.spec.ts which explicitly requires that zero-amount annual fees should not enable annual period support.
-  const renderBillingCycleControls = React.useMemo(() => plans.some(plan => Boolean(plan.annualMonthlyFee)), [plans]);
+  const renderBillingCycleControls = React.useMemo(
+    () => plans.some(plan => Boolean(plan.annualMonthlyFee && plan.annualMonthlyFee.amount > 0)),
+    [plans]
+  );📝 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 renderBillingCycleControls = React.useMemo(() => plans.some(plan => Boolean(plan.annualMonthlyFee)), [plans]); | |
| const renderBillingCycleControls = React.useMemo( | |
| () => plans.some(plan => Boolean(plan.annualMonthlyFee && plan.annualMonthlyFee.amount > 0)), | |
| [plans] | |
| ); | 
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx
around line 63, the billing-cycle control detection uses
Boolean(plan.annualMonthlyFee) which incorrectly treats a zero-amount annual fee
as enabled; change the predicate to check the numeric amount so only positive
annual fees enable the control (e.g., use plan.annualMonthlyFee?.amount > 0 or
equivalent null-safe check) so plans with {amount: 0} do not show annual billing
controls.
| const planFee = !plan.annualMonthlyFee | ||
| ? plan.fee | ||
| : planPeriod === 'annual' | ||
| ? plan.annualMonthlyFee | ||
| : plan.fee; | 
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
Fee calculation should verify amount > 0 for consistency.
The fee calculation on lines 159-163 uses presence checks (!plan.annualMonthlyFee) but should also verify the amount is greater than zero to match the expected behavior across the codebase.
-                const planFee = !plan.annualMonthlyFee
+                const planFee = !plan.annualMonthlyFee || plan.annualMonthlyFee.amount === 0
                   ? plan.fee
                   : planPeriod === 'annual'
                     ? plan.annualMonthlyFee
                     : plan.fee;🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx
around lines 159-163, the fee selection currently uses a truthy presence check
for plan.annualMonthlyFee but should ensure the amount is greater than zero;
update the ternary to explicitly check that plan.annualMonthlyFee > 0 (or
Number(plan.annualMonthlyFee) > 0) before choosing it, otherwise fall back to
plan.fee, preserving the existing planPeriod logic.
| ((subscription.planPeriod === 'month' && Boolean(subscription.plan.annualMonthlyFee)) || | ||
| subscription.planPeriod === 'annual') && | ||
| subscription.status !== 'past_due'; | 
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.
Inconsistent annual pricing checks could cause runtime errors.
Line 377 checks for annualMonthlyFee to determine if the plan is switchable, but lines 412-415 and 485-489 use non-null assertions on annualFee without verification. These are different fields, and one being present doesn't guarantee the other exists.
Either:
- Guard both fields before the switchable/fee logic executes, or
 - Use the same field consistently throughout
 
  const isSwitchable =
    ((subscription.planPeriod === 'month' && Boolean(subscription.plan.annualMonthlyFee)) ||
      subscription.planPeriod === 'annual') &&
    subscription.status !== 'past_due';
+
+ // Verify annualFee exists before building actions that use it
+ if (isSwitchable && subscription.planPeriod === 'month' && !subscription.plan.annualFee) {
+   return [];
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx around
lines 377-379 and 412-415 and 485-489, the code inconsistently checks
subscription.plan.annualMonthlyFee but later uses non-null assertions on
subscription.plan.annualFee; this mismatch can cause runtime errors. Update the
logic so the same field is used consistently (preferably choose one canonical
field across the component) or explicitly guard both fields before any
dereference: change the switchable/fee condition to verify both annualMonthlyFee
and annualFee exist where required, and replace non-null assertions with safe
conditional access or early returns when those fields are missing.
| // WHY `total_due_after_free_trial` and why `proration` | ||
| 
               | 
          
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.
Clarify the purpose of this comment.
The comment suggests uncertainty about total_due_after_free_trial and proration fields. If these fields are part of the API response but not being handled, document why they're excluded or create a task to implement them.
🤖 Prompt for AI Agents
In packages/clerk-js/src/utils/billing.ts around lines 38 to 39, the inline
comment "// WHY `total_due_after_free_trial` and why `proration`" is vague;
replace it with a clear explanation stating whether those fields appear in the
API response and why they are intentionally ignored (e.g., not used by current
billing flow), or if they must be supported, add a TODO with a link/ID to a
tracker ticket to implement handling. Include what the fields represent, the
decision (exclude or implement), and next steps (either remove/comment as
explained or add task to implement parsing/logic).
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️  Outside diff range comments (1)
packages/types/src/billing.ts (1)
691-703: Changeset must document the breaking change with explicit migration guidance.The changeset file (
./.changeset/hot-jars-smell.md) only states "Update the supported API version to2025-10-01" but fails to document the breaking change: renamingpaymentSourceIdtopaymentMethodIdinConfirmCheckoutParams.While the codebase has been correctly updated (no references to the old parameter remain), the changeset lacks the necessary migration guidance that users need to update their code. This differs from the pattern established in v4.93.0, which documented similar billing API renames with clear before/after examples.
Update the changeset to include:
- Explicit mention of the parameter rename:
 paymentSourceId→paymentMethodId- Migration guidance: "Rename
 paymentSourceIdtopaymentMethodIdin all checkoutconfirm()calls"
📜 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 selected for processing (2)
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(2 hunks)packages/types/src/billing.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
 
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{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/types/src/billing.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/types/src/billing.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/types/src/billing.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/types/src/billing.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
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor 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
Useconst assertionsfor literal types:as const
Usesatisfiesoperator 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 ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor 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/types/src/billing.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/types/src/billing.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
 - GitHub Check: Build Packages
 - GitHub Check: Analyze (javascript-typescript)
 - GitHub Check: semgrep-cloud-platform/scan
 - GitHub Check: semgrep-cloud-platform/scan
 
| * The annual price of the plan or `null` if the plan is not annual. | ||
| */ | ||
| annualFee: BillingMoneyAmount; | ||
| annualFee: BillingMoneyAmount | null; | ||
| /** | ||
| * The effective monthly price when billed annually. | ||
| * The effective monthly price when billed annually or `null` if the plan is not annual. | ||
| */ | ||
| annualMonthlyFee: BillingMoneyAmount; | ||
| annualMonthlyFee: BillingMoneyAmount | null; | 
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
Verify that consumer code was updated per previous review findings.
The nullable type change for annualFee and annualMonthlyFee is correct, but a previous review identified specific locations with non-null assertions and unsafe Boolean checks that will break with this change.
Ensure all flagged locations have been fixed before merging:
- 7 non-null assertion sites (e.g., 
SubscriptionsList.tsx:157,SubscriptionDetails/index.tsx:413,415,489) - 3 unsafe Boolean checks (e.g., 
PricingTableMatrix.tsx:63,pricing-footer-state.ts:37) 
Based on learnings
🏁 Script executed:
#!/bin/bash
# Verify that non-null assertions and unsafe Boolean checks have been addressed
echo "=== Checking for remaining non-null assertions on annual fees ==="
rg -n 'annualFee!' --type=ts -C2
echo ""
echo "=== Checking for remaining non-null assertions on annualMonthlyFee ==="
rg -n 'annualMonthlyFee!' --type=ts -C2
echo ""
echo "=== Checking for unsafe Boolean checks without null guards ==="
rg -nP 'if\s*\([^)]*\.(annualFee|annualMonthlyFee)\)' --type=ts -C2
echo ""
echo "=== Checking for direct property access without null checks ==="
rg -n '\.(annualFee|annualMonthlyFee)\.(amount|amountFormatted|currency)' --type=ts -C3Length of output: 4581
Address unsafe non-null assertions in consumer code to properly handle nullable annual fees.
The nullable type changes for annualFee and annualMonthlyFee are correct, but consumer code still uses non-null assertions (!) that bypass TypeScript's type safety. These should be replaced with proper null checks or type guards:
SubscriptionsList.tsx:157:subscription.plan.annualFee!SubscriptionDetails/index.tsx:413, 415, 489:annualFee!and property accessPricingTableDefault.tsx:294:plan.annualMonthlyFee!PaymentAttemptPage.tsx:221:plan.annualMonthlyFee!CheckoutForm.tsx:46:plan.annualMonthlyFee!
While runtime logic (e.g., planPeriod === 'annual') ensures these values exist in practice, the type system doesn't capture these guarantees. Refactor to use discriminated unions, type predicates, or explicit null checks instead of non-null assertions.
🤖 Prompt for AI Agents
In packages/types/src/billing.ts around lines 134-140, avoid forcing non-null
with `!` in consumers by either making the Plan type a discriminated union
(e.g., separate MonthlyPlan and AnnualPlan where annualFee/annualMonthlyFee
exist only on AnnualPlan with a literal period discriminator) or keep the
nullable fields but update the listed consumer files to narrow the type before
access: replace `!` usages with explicit null checks or type guards (e.g., if
(plan.period === 'annual') { /* safe to access annualFee/annualMonthlyFee */ })
so TypeScript can verify safety and remove the non-null assertions in
SubscriptionsList.tsx:157, SubscriptionDetails/index.tsx:413,415,489,
PricingTableDefault.tsx:294, PaymentAttemptPage.tsx:221, and
CheckoutForm.tsx:46.
Description
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit