Rhys Sullivanadjust pricing (#1107)907502e4 months agoHistory
81 lines · 3.1 KB · TypeScript
RawDownload
// Paid plan IDs, mirroring the Autumn plan definitions in the repo-root// `autumn.config.ts` (the deploy-time source synced via `atmn`). These IDs are// stable, so we keep them as literals here rather than importing the config// across the app boundary.export const PAID_AUTUMN_PLAN_IDS = new Set(["team", "enterprise"]); export const ACTIVE_AUTUMN_SUBSCRIPTION_STATUSES = new Set(["active", "trialing"]); // ---------------------------------------------------------------------------// Free-tier organization-creation limit — the createOrganization gate.//// These predicates read the Autumn plan config above, so they live with the// billing config (NOT in `auth/organization.ts`, which the billing-free MCP// session DO bundle reaches). Used only by `auth/handlers.ts`'s// `createOrganization` handler.// --------------------------------------------------------------------------- export const FREE_ORGANIZATIONS_PER_USER_LIMIT = 3; export type OrganizationLimitSubscriptionSummary = {  readonly planId?: string | null;  readonly status?: string | null;}; export type OrganizationLimitMembershipSummary = {  readonly organizationId: string;  readonly status?: string | null;}; export const isPaidOrganizationSubscription = (  subscription: OrganizationLimitSubscriptionSummary,): boolean =>  subscription.planId != null &&  PAID_AUTUMN_PLAN_IDS.has(subscription.planId) &&  ACTIVE_AUTUMN_SUBSCRIPTION_STATUSES.has(subscription.status ?? ""); export const hasPaidOrganizationSubscription = (  subscriptions: ReadonlyArray<OrganizationLimitSubscriptionSummary>,): boolean => subscriptions.some(isPaidOrganizationSubscription); export const shouldApplyFreeOrganizationLimit = (  activeMemberships: ReadonlyArray<OrganizationLimitMembershipSummary>,  paidOrganizationIds: ReadonlySet<string>,): boolean =>  !activeMemberships.some((membership) => paidOrganizationIds.has(membership.organizationId)); export const isOverFreeOrganizationLimit = (  activeMemberships: ReadonlyArray<OrganizationLimitMembershipSummary>,): boolean => activeMemberships.length >= FREE_ORGANIZATIONS_PER_USER_LIMIT; // ---------------------------------------------------------------------------// Per-plan member seat limits — the org member seat-gate (reserveMemberSlot).// Reads the same Autumn plan config. Used by the account provider seat-gate.// --------------------------------------------------------------------------- const MEMBER_LIMITS: Record<string, number | null> = {  free: 3,  "free-pay-as-you-go": 3,  team: null,  enterprise: null,}; export const DEFAULT_MEMBER_LIMIT = 3; export type AutumnSubscriptionSummary = {  readonly planId?: string | null;  readonly status?: string | null;}; export const selectActiveMemberLimitPlan = (  subscriptions: ReadonlyArray<AutumnSubscriptionSummary>,): string => {  const active =    subscriptions.find((subscription) =>      ACTIVE_AUTUMN_SUBSCRIPTION_STATUSES.has(subscription.status ?? ""),    ) ?? subscriptions[0];  return active?.planId ?? "free";}; export const getMemberLimitForPlan = (planId: string): number | null =>  planId in MEMBER_LIMITS ? MEMBER_LIMITS[planId] : DEFAULT_MEMBER_LIMIT;