-
Notifications
You must be signed in to change notification settings - Fork 381
chore(clerk-js, types): Cleanup naming inconsistencies in billing dates #6513
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@clerk/clerk-js': minor | ||
'@clerk/types': minor | ||
--- | ||
|
||
[Billing Beta] Cleanup naming inconsistencies in billing dates. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,13 +62,10 @@ export class CommerceSubscriptionItem extends BaseResource implements CommerceSu | |
planPeriod!: CommerceSubscriptionPlanPeriod; | ||
status!: CommerceSubscriptionStatus; | ||
createdAt!: Date; | ||
periodStart!: Date; | ||
periodEnd!: Date | null; | ||
canceledAt!: Date | null; | ||
Comment on lines
+65
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainPublic API rename: add JSDoc, confirm breaking-change handling Great move consolidating to Date-based fields. Since these are public properties, please:
Apply JSDoc locally: createdAt!: Date;
- periodStart!: Date;
- periodEnd!: Date | null;
- canceledAt!: Date | null;
+ /**
+ * Start of the current billing period.
+ */
+ periodStart!: Date;
+ /**
+ * End of the current billing period (null for free plans or when not applicable).
+ */
+ periodEnd!: Date | null;
+ /**
+ * Timestamp when cancellation was requested/effective (null if not canceled).
+ */
+ canceledAt!: Date | null; If you want temporary runtime back-compat, add in this class (types remain removed): // Deprecated runtime aliases (optional)
/** @deprecated Use periodStart */
get periodStartDate(): Date { return this.periodStart }
/** @deprecated Use periodEnd */
get periodEndDate(): Date | null { return this.periodEnd }
/** @deprecated Use canceledAt */
get canceledAtDate(): Date | null { return this.canceledAt } 🏁 Script executed: #!/usr/bin/env bash
set -e
printf "1) CommerceSubscription class definition:\n"
rg -n "class CommerceSubscription" -n packages/clerk-js/src/core/resources/CommerceSubscription.ts || echo "Not found"
printf "\n2) Date fields in CommerceSubscription:\n"
rg -nE "periodStart|periodEnd|canceledAt" -n packages/clerk-js/src/core/resources/CommerceSubscription.ts || echo "No matching fields"
printf "\n3) Changesets mentioning subscription:\n"
if [ -d ".changeset" ]; then
rg -i "subscription" -n .changeset || echo "No references in .changeset"
else
echo "No .changeset directory found"
fi
printf "\n4) Documentation mentions:\n"
if [ -d "docs" ]; then
rg -i "CommerceSubscription" -n docs || echo "No docs references"
else
echo "No docs directory found"
fi Length of output: 1125 Add JSDoc, update changeset, and docs for renamed subscription fields Please apply the following to fully cover this public-API change: • In createdAt!: Date;
+ /**
+ * Start of the current billing period.
+ */
periodStart!: Date;
+ /**
+ * End of the current billing period (null when not applicable, e.g., free plan).
+ */
periodEnd!: Date | null;
+ /**
+ * Timestamp when cancellation was requested or effective (null if not canceled).
+ */
canceledAt!: Date | null; • Ensure you’ve created or updated a • Update any public documentation or API reference to use Optional, for smoother migration you can add deprecated runtime getters: /** @deprecated Use periodStart */
get periodStartDate(): Date { return this.periodStart }
/** @deprecated Use periodEnd */
get periodEndDate(): Date | null { return this.periodEnd }
/** @deprecated Use canceledAt */
get canceledAtDate(): Date | null { return this.canceledAt } 🤖 Prompt for AI Agents
|
||
pastDueAt!: Date | null; | ||
periodStartDate!: Date; | ||
periodEndDate!: Date | null; | ||
canceledAtDate!: Date | null; | ||
periodStart!: number; | ||
periodEnd!: number; | ||
canceledAt!: number | null; | ||
//TODO(@COMMERCE): Why can this be undefined ? | ||
amount?: CommerceMoney; | ||
credit?: { | ||
|
@@ -90,16 +87,13 @@ export class CommerceSubscriptionItem extends BaseResource implements CommerceSu | |
this.plan = new CommercePlan(data.plan); | ||
this.planPeriod = data.plan_period; | ||
this.status = data.status; | ||
this.periodStart = data.period_start; | ||
this.periodEnd = data.period_end; | ||
this.canceledAt = data.canceled_at; | ||
|
||
this.createdAt = unixEpochToDate(data.created_at); | ||
this.pastDueAt = data.past_due_at ? unixEpochToDate(data.past_due_at) : null; | ||
|
||
this.periodStartDate = unixEpochToDate(data.period_start); | ||
this.periodEndDate = data.period_end ? unixEpochToDate(data.period_end) : null; | ||
this.canceledAtDate = data.canceled_at ? unixEpochToDate(data.canceled_at) : null; | ||
this.periodStart = unixEpochToDate(data.period_start); | ||
this.periodEnd = data.period_end ? unixEpochToDate(data.period_end) : null; | ||
this.canceledAt = data.canceled_at ? unixEpochToDate(data.canceled_at) : null; | ||
panteliselef marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
this.amount = data.amount ? commerceMoneyFromJSON(data.amount) : undefined; | ||
this.credit = data.credit && data.credit.amount ? { amount: commerceMoneyFromJSON(data.credit.amount) } : undefined; | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -129,7 +129,7 @@ export const usePlansContext = () => { | |||||||||||||||||||||||||||||||||||||||||||
// should the default plan be shown as active | ||||||||||||||||||||||||||||||||||||||||||||
const isDefaultPlanImplicitlyActiveOrUpcoming = useMemo(() => { | ||||||||||||||||||||||||||||||||||||||||||||
// are there no subscriptions or are all subscriptions canceled | ||||||||||||||||||||||||||||||||||||||||||||
return subscriptionItems.length === 0 || !subscriptionItems.some(subscription => !subscription.canceledAtDate); | ||||||||||||||||||||||||||||||||||||||||||||
return subscriptionItems.length === 0 || !subscriptionItems.some(subscription => !subscription.canceledAt); | ||||||||||||||||||||||||||||||||||||||||||||
}, [subscriptionItems]); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
// return the active or upcoming subscription for a plan if it exists | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -178,7 +178,7 @@ export const usePlansContext = () => { | |||||||||||||||||||||||||||||||||||||||||||
({ plan, subscription: sub }: { plan?: CommercePlanResource; subscription?: CommerceSubscriptionItemResource }) => { | ||||||||||||||||||||||||||||||||||||||||||||
const subscription = sub ?? (plan ? activeOrUpcomingSubscription(plan) : undefined); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
return !subscription || !subscription.canceledAtDate; | ||||||||||||||||||||||||||||||||||||||||||||
return !subscription || !subscription.canceledAt; | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
[activeOrUpcomingSubscription], | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -214,7 +214,7 @@ export const usePlansContext = () => { | |||||||||||||||||||||||||||||||||||||||||||
const getLocalizationKey = () => { | ||||||||||||||||||||||||||||||||||||||||||||
// Handle subscription cases | ||||||||||||||||||||||||||||||||||||||||||||
if (subscription) { | ||||||||||||||||||||||||||||||||||||||||||||
if (_selectedPlanPeriod !== subscription.planPeriod && subscription.canceledAtDate) { | ||||||||||||||||||||||||||||||||||||||||||||
if (_selectedPlanPeriod !== subscription.planPeriod && subscription.canceledAt) { | ||||||||||||||||||||||||||||||||||||||||||||
if (_selectedPlanPeriod === 'month') { | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('commerce.switchToMonthly'); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -224,7 +224,7 @@ export const usePlansContext = () => { | |||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
if (subscription.canceledAtDate) { | ||||||||||||||||||||||||||||||||||||||||||||
if (subscription.canceledAt) { | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('commerce.reSubscribe'); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
|
@@ -268,14 +268,14 @@ export const usePlansContext = () => { | |||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
if (subscription.status === 'upcoming') { | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('badge__startsAt', { date: subscription.periodStartDate }); | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('badge__startsAt', { date: subscription.periodStart }); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
if (subscription.canceledAtDate) { | ||||||||||||||||||||||||||||||||||||||||||||
if (subscription.canceledAt) { | ||||||||||||||||||||||||||||||||||||||||||||
// @ts-expect-error `periodEndDate` is always defined when `canceledAtDate` exists | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('badge__canceledEndsAt', { date: subscription.periodEndDate }); | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('badge__canceledEndsAt', { date: subscription.periodEnd }); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
if (subscription.periodEndDate) { | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('badge__renewsAt', { date: subscription.periodEndDate }); | ||||||||||||||||||||||||||||||||||||||||||||
if (subscription.periodEnd) { | ||||||||||||||||||||||||||||||||||||||||||||
return localizationKeys('badge__renewsAt', { date: subscription.periodEnd }); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+271
to
279
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Remove stale ts-expect-error and add a type-safe guard The Apply: - if (subscription.canceledAt) {
- // @ts-expect-error `periodEndDate` is always defined when `canceledAtDate` exists
- return localizationKeys('badge__canceledEndsAt', { date: subscription.periodEnd });
- }
+ if (subscription.canceledAt && subscription.periodEnd) {
+ return localizationKeys('badge__canceledEndsAt', { date: subscription.periodEnd });
+ } This reflects the new invariant and keeps the code type-safe. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||
}, []); | ||||||||||||||||||||||||||||||||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.