-
Notifications
You must be signed in to change notification settings - Fork 370
chore(clerk-js,types): Update checkout flow to support free trials #6494
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?
chore(clerk-js,types): Update checkout flow to support free trials #6494
Conversation
🦋 Changeset detectedLatest commit: 1c6b47c The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 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 Git ↗︎
|
📝 WalkthroughWalkthroughThis change set introduces comprehensive support for free trial functionality across multiple packages and components. New properties related to free trial status, eligibility, and end dates are added to commerce-related TypeScript interfaces, JSON types, and classes. UI components such as checkout forms and pricing tables are updated to display free trial information, including badges, trial end dates, and trial-specific messaging. Localization resources are extended with new keys for free trial-related labels and notices. The logic for determining button labels and displaying plan information is centralized and enhanced to reflect free trial states. Changesets document minor version updates for affected packages. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–25 minutes Complexity label: Moderate Note 🔌 MCP (Model Context Protocol) integration is now available in Early Access!Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 11
🧹 Nitpick comments (12)
.changeset/sour-lemons-talk.md (1)
6-6
: Consider a more descriptive summary for release notes.
E.g., “Add free trial fields to CommerceCheckout and related resources; wire into UI and localization.”.changeset/tender-planets-win.md (1)
7-7
: Nit: Expand the message for clarity.
E.g., “PricingTable: show trial footer notice and date when free trials are enabled; add localization keys.”packages/clerk-js/src/core/resources/CommerceCheckout.ts (1)
26-26
: Document new public property with JSDoc.
Add succinct JSDoc forfreeTrialEndsAt
to clarify source and timezone.Apply:
export class CommerceCheckout extends BaseResource implements CommerceCheckoutResource { @@ - freeTrialEndsAt!: Date | null; + /** + * End date of the free trial for this checkout, or null if not on trial. + * Derived from API field `free_trial_ends_at` (unix epoch seconds) and converted to a JS Date in UTC. + */ + freeTrialEndsAt!: Date | null;packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (1)
238-244
: Localization key selection is correct; minor tidy and tests suggested.
- Redundant optional chaining on
subscription
inside a guarded branch; optional.- Please add tests covering footer behavior for: upcoming, active with trial in future, active with trial in past, and active with period mismatch.
I can draft component tests (React Testing Library) for these scenarios if helpful.
packages/clerk-js/src/core/resources/CommercePlan.ts (2)
30-31
: Public API docs for new fields.Add JSDoc on the new properties to keep the public API self-documented and discoverable.
export class CommercePlan extends BaseResource implements CommercePlanResource { ... - freeTrialDays!: number | null; - freeTrialEnabled!: boolean; + /** Number of free trial days for this plan; null when no trial is configured. */ + freeTrialDays!: number | null; + /** Whether free trials are enabled for this plan. */ + freeTrialEnabled!: boolean;
33-36
: Tests missing for new fields.No tests were provided for mapping free trial fields from JSON and snapshot parity. Please add minimal unit tests covering:
- fromJSON sets freeTrialDays/freeTrialEnabled defaults and values.
- __internal_toSnapshot includes these fields (when you add them).
packages/clerk-js/src/core/resources/CommerceSubscription.ts (1)
81-85
: Tests recommended for free trial mapping and date conversion.Add tests verifying:
- eligibleForFreeTrial default/value mapping.
- freeTrialEndsAt date conversion with undefined/null present.
- Stability of behavior when JSON omits experimental fields.
Also applies to: 86-113
packages/types/src/json.ts (3)
785-788
: Remove commented-out schema line.Avoid commented schema fields in published types.
- // is_free_trial: boolean; // TODO(@COMMERCE): Remove optional after GA. free_trial_ends_at?: number | null;
817-818
: Subscription JSON addition approved; mark experimental for clarity.Consider annotating with an inline JSDoc to match surrounding experimental notes.
export interface CommerceSubscriptionJSON extends ClerkResourceJSON { ... - eligible_for_free_trial?: boolean; + /** @experimental */ + eligible_for_free_trial?: boolean;
882-884
: Checkout JSON addition approved; keep GA note, add JSDoc.Matches the item JSON pattern.
export interface CommerceCheckoutJSON extends ClerkResourceJSON { ... - // TODO(@COMMERCE): Remove optional after GA. - free_trial_ends_at?: number | null; + // TODO(@COMMERCE): Remove optional after GA. + /** @experimental */ + free_trial_ends_at?: number | null;packages/clerk-js/src/ui/components/Subscriptions/badge.tsx (1)
31-37
: Stale@ts-expect-error
commentThe comment still mentions “
ended
is included” while the suppressed key is now"free_trial"
.
Refresh the comment (or delete it once the enum is fixed) to avoid confusion.packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx (1)
56-60
: Minor – avoid rendering empty badge container
SubscriptionBadge
is rendered inside the JSX conditional, but the parent passesnull
when the condition fails, leaving an unnecessarybadge={null}
prop.-badge={ - plan.freeTrialEnabled && freeTrialEndsAt ? ( - <SubscriptionBadge subscription={{ status: 'free_trial' }} /> - ) : null -} +badge={ + plan.freeTrialEnabled && freeTrialEndsAt + ? <SubscriptionBadge subscription={{ status: 'free_trial' }} /> + : undefined +}Passing
undefined
spares React a noop render.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
.changeset/sour-lemons-talk.md
(1 hunks).changeset/tender-planets-win.md
(1 hunks)packages/clerk-js/src/core/resources/CommerceCheckout.ts
(3 hunks)packages/clerk-js/src/core/resources/CommercePlan.ts
(2 hunks)packages/clerk-js/src/core/resources/CommerceSubscription.ts
(4 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
(10 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
(2 hunks)packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
(1 hunks)packages/clerk-js/src/ui/contexts/components/Plans.tsx
(4 hunks)packages/clerk-js/src/ui/elements/LineItems.tsx
(2 hunks)packages/localizations/src/en-US.ts
(4 hunks)packages/shared/src/react/hooks/useCheckout.ts
(1 hunks)packages/types/src/commerce.ts
(4 hunks)packages/types/src/json.ts
(4 hunks)packages/types/src/localization.ts
(3 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
.changeset/**
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Automated releases must use Changesets.
Files:
.changeset/tender-planets-win.md
.changeset/sour-lemons-talk.md
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.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/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.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
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.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/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
**/*
⚙️ CodeRabbit Configuration File
If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.
Files:
packages/shared/src/react/hooks/useCheckout.ts
packages/clerk-js/src/core/resources/CommercePlan.ts
packages/clerk-js/src/core/resources/CommerceCheckout.ts
packages/clerk-js/src/ui/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/types/src/json.ts
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/core/resources/CommerceSubscription.ts
packages/types/src/commerce.ts
packages/types/src/localization.ts
packages/localizations/src/en-US.ts
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
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/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.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/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.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/elements/LineItems.tsx
packages/clerk-js/src/ui/contexts/components/Plans.tsx
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
packages/localizations/**/*
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Localization files must be placed in 'packages/localizations/'.
Files:
packages/localizations/src/en-US.ts
**/localizations/**/*.ts
⚙️ CodeRabbit Configuration File
**/localizations/**/*.ts
: Review the changes to localization files with the following guidelines:
- Ensure that no existing translations are accidentally removed unless they are being replaced or fixed. If a string is removed, verify that it is intentional and justified.
- Check that all translations are friendly, formal, or semi-formal. Explicit, offensive, or inappropriate language is not allowed. If you find any potentially offensive language or are unsure, tag the @clerk/sdk-infra team in a separate comment. If you do not intend to tag the team, refer to it as "Clerk SDK Infra team" instead.
- Use the most up-to-date base localization file (https://github.com/clerk/javascript/blob/main/packages/localizations/src/en-US.ts) to validate changes, ensuring consistency and completeness.
- Confirm that new translations are accurate, contextually appropriate, and match the intent of the original English strings.
- Check for formatting issues, such as missing placeholders, incorrect variable usage, or syntax errors.
- Ensure that all keys are unique and that there are no duplicate or conflicting entries.
- If you notice missing translations for new keys, flag them for completion.
Files:
packages/localizations/src/en-US.ts
🧠 Learnings (1)
📚 Learning: 2025-07-22T08:43:52.095Z
Learnt from: panteliselef
PR: clerk/javascript#6317
File: packages/clerk-js/src/ui/contexts/components/Plans.tsx:56-68
Timestamp: 2025-07-22T08:43:52.095Z
Learning: The `useSubscription` hook exported from `packages/clerk-js/src/ui/contexts/components/Plans.tsx` is only used internally within clerk-js UI components and is not exposed to external consumers, making renames and modifications to this hook non-breaking for end users.
Applied to files:
packages/clerk-js/src/ui/contexts/components/Plans.tsx
🧬 Code Graph Analysis (2)
packages/clerk-js/src/ui/components/Subscriptions/badge.tsx (1)
packages/types/src/commerce.ts (1)
CommerceSubscriptionItemResource
(994-1146)
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (1)
packages/clerk-js/src/ui/localization/localizationKeys.ts (1)
localizationKeys
(72-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: semgrep/ci
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (8)
.changeset/sour-lemons-talk.md (1)
1-4
: Changeset frontmatter looks correct for a minor bump.
No structural issues detected..changeset/tender-planets-win.md (1)
1-5
: Minor bumps are appropriate for these packages.
Scope aligns with UI + types + localization updates.packages/clerk-js/src/core/resources/CommerceCheckout.ts (2)
10-11
: Import for date conversion is appropriate.
Alias usage matches other parts of the repo; no issues.
49-49
: Approve mapping and types
All checks passed—free_trial_ends_at
is defined onCommerceCheckoutJSON
andfreeTrialEndsAt: Date | null
exists onCommerceCheckoutResource
. The conversion viaunixEpochToDate
mirrors the pattern inCommerceSubscription
. No changes required.packages/types/src/localization.ts (1)
146-146
: All new localization keys are present and correctly parameterized
- Verified that packages/localizations/src/en-US.ts defines:
- badge__freeTrial
- badge__trialEndsAt (uses
{{ date }}
)- startFreeTrial
- startFreeTrial__days (uses
{{ days }}
)- totalDueAfterTrial (uses
{{ days }}
)- Confirmed types in packages/types/src/localization.ts match:
- badge__trialEndsAt:
LocalizationValue<'date'>
- startFreeTrial__days:
LocalizationValue<'days'>
- totalDueAfterTrial:
LocalizationValue<'days'>
- Call sites supply the correct params (
date
for trialEndsAt,days
for days-based keys).No further changes needed.
packages/types/src/json.ts (2)
652-654
: All new JSON fields are consumed.
Verified thatfree_trial_days
,free_trial_enabled
,eligible_for_free_trial
, andfree_trial_ends_at
are referenced in both core resources and all relevant UI components—no further action needed.
652-654
: Resource mapping confirmed for new optional JSON fieldsThe
free_trial_days
andfree_trial_enabled
properties are correctly handled inCommercePlan
:
- packages/clerk-js/src/core/resources/CommercePlan.ts:
• Line 61:this.freeTrialDays = this.withDefault(data.free_trial_days, null);
• Line 62:this.freeTrialEnabled = this.withDefault(data.free_trial_enabled, false);
No further changes needed.
packages/localizations/src/en-US.ts (1)
43-48
: Localization keys look good – remember to add translations for other localesNew keys and placeholders are syntactically correct and non-duplicated.
Ensure equivalent entries are added to every supported locale before release.Also applies to: 92-93, 148-150
this.freeTrialDays = this.withDefault(data.free_trial_days, null); | ||
this.freeTrialEnabled = this.withDefault(data.free_trial_enabled, false); | ||
this.features = (data.features || []).map(feature => new CommerceFeature(feature)); |
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.
💡 Verification agent
🧩 Analysis chain
Snapshot parity: include free trial fields in __internal_toSnapshot.
Downstream consumers that rely on snapshots will miss these new fields. Include them (and update the corresponding snapshot type if needed).
public __internal_toSnapshot(): CommercePlanJSONSnapshot {
return {
object: 'commerce_plan',
id: this.id,
name: this.name,
amount: this.amount,
amount_formatted: this.amountFormatted,
annual_amount: this.annualAmount,
annual_amount_formatted: this.annualAmountFormatted,
annual_monthly_amount: this.annualMonthlyAmount,
annual_monthly_amount_formatted: this.annualMonthlyAmountFormatted,
currency: this.currency,
currency_symbol: this.currencySymbol,
description: this.description,
is_default: this.isDefault,
is_recurring: this.isRecurring,
has_base_fee: this.hasBaseFee,
for_payer_type: this.forPayerType,
publicly_visible: this.publiclyVisible,
slug: this.slug,
avatar_url: this.avatarUrl,
+ free_trial_days: this.freeTrialDays,
+ free_trial_enabled: this.freeTrialEnabled,
features: this.features.map(feature => feature.__internal_toSnapshot()),
};
}
To confirm types and call sites:
Also applies to: 68-91
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check if the snapshot type includes the new fields
rg -n "CommercePlanJSONSnapshot" packages | sed -n '1,200p'
# Check if code reads the new fields from snapshots
rg -n "free_trial_days|free_trial_enabled" packages
Length of output: 1029
Add free trial fields to __internal_toSnapshot
for snapshot parity
We verified that the CommercePlanJSONSnapshot
type already includes
free_trial_days?: number | null;
free_trial_enabled?: boolean;
so no type updates are needed. However, the two new properties must be
exported in the snapshot method:
• File: packages/clerk-js/src/core/resources/CommercePlan.ts
• Method: public __internal_toSnapshot(): CommercePlanJSONSnapshot
Apply this diff:
slug: this.slug,
avatar_url: this.avatarUrl,
+ free_trial_days: this.freeTrialDays,
+ free_trial_enabled: this.freeTrialEnabled,
features: this.features.map(feature => feature.__internal_toSnapshot()),
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/CommercePlan.ts around lines 61 to 63,
the __internal_toSnapshot method is missing the freeTrialDays and
freeTrialEnabled properties in its returned snapshot object. Update the
__internal_toSnapshot method to include these two properties so that the
snapshot output matches the CommercePlanJSONSnapshot type and maintains parity
with the data model.
eligibleForFreeTrial?: boolean; | ||
|
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.
💡 Verification agent
🧩 Analysis chain
Align typing and default for eligibleForFreeTrial.
Currently typed as optional and directly assigned, which can propagate undefined. Consider one of:
- Option A (preferred): make it non-optional boolean and default to false via withDefault.
- Option B: keep optional but still default to false to simplify consumers (then consider tightening type later).
Option A:
- eligibleForFreeTrial?: boolean;
+ eligibleForFreeTrial!: boolean;
...
- this.eligibleForFreeTrial = data.eligible_for_free_trial;
+ this.eligibleForFreeTrial = this.withDefault(data.eligible_for_free_trial, false);
To gauge impact, search call sites:
Also applies to: 55-56
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "eligibleForFreeTrial" packages
Length of output: 494
Align eligibleForFreeTrial typing and default to false
To ensure consumers always get a boolean (never undefined
), update both the runtime resource and the TS definitions:
- In
packages/clerk-js/src/core/resources/CommerceSubscription.ts
// before
- eligibleForFreeTrial?: boolean;
- eligibleForFreeTrial!: boolean;
…
constructor(data: API.CommerceSubscription) {
…
- this.eligibleForFreeTrial = data.eligible_for_free_trial;
- this.eligibleForFreeTrial = this.withDefault(data.eligible_for_free_trial, false);
}
- In `packages/types/src/commerce.ts` (around line 1264)
```diff
export interface CommerceSubscription {
…
- eligibleForFreeTrial?: boolean;
+ eligibleForFreeTrial: boolean;
…
}
This change makes eligibleForFreeTrial
a required boolean with a default of false
, removing any need for downstream undefined checks.
📝 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.
eligibleForFreeTrial?: boolean; | |
// packages/clerk-js/src/core/resources/CommerceSubscription.ts | |
export class CommerceSubscription extends Resource { | |
// … | |
- eligibleForFreeTrial?: boolean; | |
+ eligibleForFreeTrial!: boolean; | |
// … | |
constructor(data: API.CommerceSubscription) { | |
super(); | |
// … | |
- this.eligibleForFreeTrial = data.eligible_for_free_trial; | |
+ this.eligibleForFreeTrial = this.withDefault(data.eligible_for_free_trial, false); | |
// … | |
} | |
} |
eligibleForFreeTrial?: boolean; | |
// packages/types/src/commerce.ts | |
export interface CommerceSubscription { | |
// … | |
- eligibleForFreeTrial?: boolean; | |
+ eligibleForFreeTrial: boolean; | |
// … | |
} |
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/CommerceSubscription.ts around lines 30
to 31, update the eligibleForFreeTrial property to be a required boolean instead
of optional. Set its default value to false in the runtime resource to ensure it
is never undefined. Also, update the TypeScript type definition in
packages/types/src/commerce.ts near line 1264 to reflect eligibleForFreeTrial as
a non-optional boolean. This will guarantee consumers always receive a boolean
value without needing to check for undefined.
this.freeTrialEndsAt = data.free_trial_ends_at ? unixEpochToDate(data.free_trial_ends_at) : null; | ||
return this; |
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
Use nullish check when mapping timestamps.
Avoid truthy checks for numeric timestamps; use a nullish check to prevent edge-case misclassification.
- this.freeTrialEndsAt = data.free_trial_ends_at ? unixEpochToDate(data.free_trial_ends_at) : null;
+ this.freeTrialEndsAt =
+ data.free_trial_ends_at != null ? unixEpochToDate(data.free_trial_ends_at) : null;
📝 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.
this.freeTrialEndsAt = data.free_trial_ends_at ? unixEpochToDate(data.free_trial_ends_at) : null; | |
return this; | |
this.freeTrialEndsAt = | |
data.free_trial_ends_at != null ? unixEpochToDate(data.free_trial_ends_at) : null; | |
return this; |
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/CommerceSubscription.ts at lines 110 to
111, replace the truthy check on data.free_trial_ends_at with a nullish check
(e.g., data.free_trial_ends_at != null) to correctly handle numeric timestamps
that could be zero or falsy but valid. This ensures the timestamp is only
considered absent if it is null or undefined, preventing misclassification of
valid zero values.
{freeTrialEndsAt && plan.freeTrialDays && ( | ||
<LineItems.Group variant='tertiary'> | ||
<LineItems.Title | ||
title={localizationKeys('commerce.checkout.totalDueAfterTrial', { | ||
days: plan.freeTrialDays, | ||
})} | ||
/> | ||
<LineItems.Description | ||
text={`${totals.grandTotal?.currencySymbol}${totals.grandTotal?.amountFormatted}`} | ||
/> | ||
</LineItems.Group> | ||
)} |
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
Handle missing freeTrialDays
gracefully
The label always injects plan.freeTrialDays
; when it’s null
the UI will show “after trial ends in null days”.
Guard the value or fall back to commerce.checkout.totalDueAfterTrial
without the days
variable.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx around lines 95
to 106, the code injects plan.freeTrialDays into the label without checking if
it is null, causing the UI to display "null" days. To fix this, add a
conditional check to only pass the days variable if plan.freeTrialDays is a
valid number; otherwise, render the label without the days variable to avoid
showing "null" in the UI.
const useSubmitLabel = () => { | ||
const { checkout } = useCheckout(); | ||
const { status, totals } = checkout; | ||
const { status, freeTrialEndsAt, totals } = checkout; | ||
|
||
if (status === 'needs_initialization') { | ||
return null; | ||
throw new Error('Clerk: Invalid state'); | ||
} | ||
|
||
if (freeTrialEndsAt) { | ||
return localizationKeys('commerce.startFreeTrial'); | ||
} | ||
|
||
if (totals.totalDueNow.amount > 0) { | ||
return localizationKeys('commerce.pay', { | ||
amount: `${totals.totalDueNow.currencySymbol}${totals.totalDueNow.amountFormatted}`, | ||
}); | ||
} | ||
|
||
return localizationKeys('commerce.subscribe'); | ||
}; |
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
useSubmitLabel
can throw at runtime
Throwing when status === 'needs_initialization'
propagates to the component tree and crashes the checkout drawer if the hook is called too early.
Return a safe placeholder instead (e.g., '...'
) until the state stabilises, or guard the hook’s usage.
- if (status === 'needs_initialization') {
- throw new Error('Clerk: Invalid state');
- }
+ if (status === 'needs_initialization') {
+ return localizationKeys('formButtonPrimary'); // fallback label
+ }
Avoids unhandled exceptions in edge cases.
📝 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 useSubmitLabel = () => { | |
const { checkout } = useCheckout(); | |
const { status, totals } = checkout; | |
const { status, freeTrialEndsAt, totals } = checkout; | |
if (status === 'needs_initialization') { | |
return null; | |
throw new Error('Clerk: Invalid state'); | |
} | |
if (freeTrialEndsAt) { | |
return localizationKeys('commerce.startFreeTrial'); | |
} | |
if (totals.totalDueNow.amount > 0) { | |
return localizationKeys('commerce.pay', { | |
amount: `${totals.totalDueNow.currencySymbol}${totals.totalDueNow.amountFormatted}`, | |
}); | |
} | |
return localizationKeys('commerce.subscribe'); | |
}; | |
const useSubmitLabel = () => { | |
const { checkout } = useCheckout(); | |
const { status, freeTrialEndsAt, totals } = checkout; | |
if (status === 'needs_initialization') { | |
return localizationKeys('formButtonPrimary'); // fallback label | |
} | |
if (freeTrialEndsAt) { | |
return localizationKeys('commerce.startFreeTrial'); | |
} | |
if (totals.totalDueNow.amount > 0) { | |
return localizationKeys('commerce.pay', { | |
amount: `${totals.totalDueNow.currencySymbol}${totals.totalDueNow.amountFormatted}`, | |
}); | |
} | |
return localizationKeys('commerce.subscribe'); | |
}; |
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx around lines
299 to 318, the useSubmitLabel hook throws an error when status is
'needs_initialization', which can crash the component tree if called too early.
Instead of throwing, modify the hook to return a safe placeholder string like
'...' in this state to prevent unhandled exceptions and allow the UI to
stabilize gracefully.
export const SubscriptionBadge = <T extends { status: CommerceSubscriptionItemResource['status'] }>({ | ||
subscription, | ||
elementDescriptor, | ||
}: { | ||
subscription: CommerceSubscriptionItemResource; | ||
subscription: T | { status: 'free_trial' }; | ||
elementDescriptor?: ElementDescriptor; |
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
Inconsistent typing for status
– consider extending the core enum instead of local work-arounds
The component now unions { status: 'free_trial' }
to work around the fact that CommerceSubscriptionStatus
doesn’t include "free_trial"
.
This leaks an ad-hoc type downstream and forces the @ts-expect-error
casts later on.
Prefer updating CommerceSubscriptionStatus
(and upstream API) to include "free_trial"
and keep the prop simply subscription: { status: CommerceSubscriptionStatus }
.
This restores compile-time safety and removes the need for the union / ignores.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/Subscriptions/badge.tsx around lines 20
to 25, the subscription prop type uses a union to add 'free_trial' status
separately, causing inconsistent typing and requiring @ts-expect-error casts. To
fix this, update the CommerceSubscriptionStatus enum in the core types and
upstream API to include 'free_trial' as a valid status, then change the
subscription prop type to use only { status: CommerceSubscriptionStatus }
without unions. This will ensure consistent typing and remove the need for type
ignores.
const freeTrialOr = (localizationKey: LocalizationKey): LocalizationKey => { | ||
if (plan?.freeTrialEnabled && topLevelSubscription?.eligibleForFreeTrial) { | ||
return localizationKeys('commerce.startFreeTrial__days', { days: plan.freeTrialDays ?? 0 }); | ||
} | ||
return localizationKey; | ||
}; | ||
|
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
Guard against zero / null trial days
freeTrialOr
interpolates plan.freeTrialDays ?? 0
.
If the backend returns 0
or null
, the UI will generate “Start 0-day free trial”, which looks broken.
- return localizationKeys('commerce.startFreeTrial__days', { days: plan.freeTrialDays ?? 0 });
+const days = plan.freeTrialDays ?? 0;
+return days > 0
+ ? localizationKeys('commerce.startFreeTrial__days', { days })
+ : localizationKeys('commerce.startFreeTrial');
This keeps the copy natural when the duration is unknown or disabled.
📝 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 freeTrialOr = (localizationKey: LocalizationKey): LocalizationKey => { | |
if (plan?.freeTrialEnabled && topLevelSubscription?.eligibleForFreeTrial) { | |
return localizationKeys('commerce.startFreeTrial__days', { days: plan.freeTrialDays ?? 0 }); | |
} | |
return localizationKey; | |
}; | |
const freeTrialOr = (localizationKey: LocalizationKey): LocalizationKey => { | |
if (plan?.freeTrialEnabled && topLevelSubscription?.eligibleForFreeTrial) { | |
const days = plan.freeTrialDays ?? 0; | |
return days > 0 | |
? localizationKeys('commerce.startFreeTrial__days', { days }) | |
: localizationKeys('commerce.startFreeTrial'); | |
} | |
return localizationKey; | |
}; |
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/contexts/components/Plans.tsx around lines 215 to
221, the function freeTrialOr uses plan.freeTrialDays ?? 0, which can produce
"Start 0-day free trial" if freeTrialDays is 0 or null. Update the logic to
check if freeTrialDays is a positive number before interpolating it; if not,
return the original localizationKey to avoid displaying unnatural "0-day" trial
text.
badge?: React.ReactNode; | ||
} |
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.
💡 Verification agent
🧩 Analysis chain
Badge slot theming/localization contract; add JSDoc (and consider a theming wrapper).
Good addition. To align with our UI guidelines:
- Document that callers must pass a localized component and that theming should be handled by that component (or expose a theming slot).
- Optionally wrap the badge with a dedicated element descriptor to enable stable theming of the slot.
Apply JSDoc on the prop (Lines 87-88):
interface TitleProps {
title?: string | LocalizationKey;
description?: string | LocalizationKey;
icon?: React.ComponentType;
- badge?: React.ReactNode;
+ /**
+ * Optional badge element rendered next to the title.
+ * Caller is responsible for localization and theming of the badge contents (e.g. pass <SubscriptionBadge />).
+ */
+ badge?: React.ReactNode;
}
Optional theming slot wrapper (Lines 124-125) — requires adding a corresponding descriptor (e.g. lineItemsTitleBadge) to descriptors before use:
- {badge}
+ {/* wrap to expose a stable theming hook */}
+ {badge ? (
+ <Span elementDescriptor={descriptors.lineItemsTitleBadge}>
+ {badge}
+ </Span>
+ ) : null}
To ensure consistency, verify the SubscriptionBadge component uses elementDescriptor and localized strings:
Also applies to: 90-90, 124-125
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify the badge implementation uses elementDescriptor and localization
fd badge.tsx packages | xargs -I {} sh -c 'echo "----- {}"; sed -n "1,200p" "{}" | rg -n "elementDescriptor|localizationKey|localizationKeys" -n || true'
Length of output: 1468
Document badge
prop and expose a theming slot
The SubscriptionBadge
component already accepts an elementDescriptor
and uses localizationKeys
, so adding a JSDoc and a wrapper here will align with our UI guidelines.
• At Line 87–88 (TitleProps
), add:
interface TitleProps {
title?: string | LocalizationKey;
description?: string | LocalizationKey;
icon?: React.ComponentType;
- badge?: React.ReactNode;
+ /**
+ * Optional badge element rendered next to the title.
+ * The caller is responsible for providing a localized and themed badge
+ * (e.g. `<SubscriptionBadge elementDescriptor={descriptors.lineItemsTitleBadge} />`).
+ */
+ badge?: React.ReactNode;
}
• At the render site (around Line 124–125), wrap badge
in a stable theming slot:
- {badge}
+ {badge ? (
+ <Span elementDescriptor={descriptors.lineItemsTitleBadge}>
+ {badge}
+ </Span>
+ ) : null}
This ensures consumers can target lineItemsTitleBadge
in our design system for consistent theming.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/elements/LineItems.tsx around lines 87 to 88, add a
JSDoc comment to document the `badge` prop in the `TitleProps` interface
explaining its purpose and usage. Then, around lines 124 to 125 where `badge` is
rendered, wrap it inside a stable theming slot component named
`lineItemsTitleBadge` to expose a theming slot for consistent styling according
to UI guidelines.
@@ -109,6 +109,7 @@ export const useCheckout = (options?: Params): __experimental_UseCheckoutReturn | |||
planPeriod: null, | |||
plan: null, | |||
paymentSource: null, | |||
freeTrialEndsAt: 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.
💡 Verification agent
🧩 Analysis chain
Good addition: include freeTrialEndsAt
in fallback state.
Prevents undefined access and keeps the fallback shape aligned with the resource.
Run this script to confirm types and JSON contracts include the new property everywhere:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Search for types and JSON fields for free trial support..."
rg -n "CommerceCheckoutJSON|CommerceCheckoutResource" -A 40 packages | sed -n '1,200p' || true
rg -n "free_trial_ends_at|freeTrialEndsAt" packages || true
echo "Ensure localization keys exist for trial UI:"
rg -n "badge__trialEndsAt|badge__startsAt" packages/localizations || true
Length of output: 20507
Approved: freeTrialEndsAt
Fallback Aligns With Types—Missing Localization Keys
All core types and resources correctly include and map freeTrialEndsAt
(JSON: free_trial_ends_at?: number | null
; TS: freeTrialEndsAt: Date | null
), and the null fallback in useCheckout.ts
prevents undefined access. Core resource classes and UI components already guard and render this field.
However, the localization key for “Trial ends” only exists in en-US
. Please add badge__trialEndsAt
with the appropriate translation to each locale:
- Location:
packages/localizations/src/*.ts
- Key to add alongside
badge__startsAt
in each file:badge__trialEndsAt: "<localized ‘Trial ends {{ date | shortDate(...) }}’>",
🤖 Prompt for AI Agents
In packages/shared/src/react/hooks/useCheckout.ts at line 112, the
freeTrialEndsAt fallback is correctly set to null, but the localization key
badge__trialEndsAt is missing in all locale files except en-US. To fix this, add
the badge__trialEndsAt key with the appropriate localized string for "Trial ends
{{ date | shortDate(...) }}" alongside badge__startsAt in each localization file
under packages/localizations/src/*.ts.
/** | ||
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. | ||
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes. | ||
* @example | ||
* ```tsx | ||
* <ClerkProvider clerkJsVersion="x.x.x" /> | ||
* ``` | ||
*/ | ||
freeTrialDays: number | null; | ||
/** | ||
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. | ||
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes. | ||
* @example | ||
* ```tsx | ||
* <ClerkProvider clerkJsVersion="x.x.x" /> | ||
* ``` | ||
*/ | ||
freeTrialEnabled: boolean; | ||
__internal_toSnapshot: () => CommercePlanJSONSnapshot; |
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.
Enum drift – add "free_trial"
to CommerceSubscriptionStatus
UI components rely on a "free_trial"
status yet the source of truth (CommerceSubscriptionStatus
) still omits it.
Add the literal to the union so SDK consumers and the UI stay type-safe.
-export type CommerceSubscriptionStatus = 'active' | 'ended' | 'upcoming' | 'past_due';
+export type CommerceSubscriptionStatus =
+ | 'active'
+ | 'ended'
+ | 'upcoming'
+ | 'past_due'
+ | 'free_trial';
Afterward, remove the local union workaround in SubscriptionBadge
.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/types/src/commerce.ts around lines 440 to 458, the
CommerceSubscriptionStatus union type is missing the "free_trial" literal, which
causes type-safety issues for UI components relying on this status. Add the
string literal "free_trial" to the CommerceSubscriptionStatus union type to keep
the SDK and UI components consistent and type-safe. After this update, remove
any local union type workarounds related to this status in the SubscriptionBadge
component.
Description
Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change
Summary by CodeRabbit
New Features
Localization
Bug Fixes
Documentation