mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
409a0277fb
* fix: Convert BillingPlan enum to const object to prevent webpack tree-shaking TypeScript enums compile to IIFEs which can be incorrectly tree-shaken by webpack when 'sideEffects: false' is set in package.json. Converting to a const object with 'as const' avoids the IIFE pattern while maintaining full type safety. This fixes the 'TRPCError: BillingPlan is not defined' error that occurred in the hasTeamPlan tRPC handler. Follows the existing CHECKOUT_SESSION_TYPES pattern in the same file. Co-Authored-By: [email protected] <[email protected]> * fix: Re-export BillingPlan from billing-plans to prevent tree-shaking The issue was a module initialization order problem. When BillingPlan was imported from constants.ts into billing-plans.ts and used inside the BillingPlanService class, webpack's tree-shaker (with sideEffects: false) couldn't properly track the value dependency across the package boundary. By re-exporting BillingPlan from the same module that exports BillingPlanService, we ensure the constant is bundled together with the class that uses it, preventing tree-shaking. Co-Authored-By: [email protected] <[email protected]> * revert: Restore BillingPlan enum format in constants.ts The const object conversion didn't fix the tree-shaking issue. The real fix is re-exporting BillingPlan from billing-plans.ts to ensure it's part of the same module as BillingPlanService. Co-Authored-By: [email protected] <[email protected]> * fix: Add BillingPlan as private static member to prevent tree-shaking By making BillingPlan a private static member of BillingPlanService, webpack now sees it as part of the class definition rather than just used inside methods. This creates a strong reference that prevents webpack's tree-shaker from removing the enum initialization IIFE when 'sideEffects: false' is set. With the previous approach where BillingPlan was only imported and used inside methods, webpack's static analysis couldn't properly track the enum usage across package boundaries (@calcom/features -> @calcom/trpc), causing it to incorrectly determine the enum initialization was unused code. Fixes the TRPCError: BillingPlan is not defined runtime error. Thread: https://calendso.slack.com/archives/C08LT9BLEET/p1759420015428149 Co-Authored-By: [email protected] <[email protected]> * fix: Clean up BillingPlan references and add explanatory comment - Remove unnecessary re-export statement - Use direct BillingPlan references instead of BillingPlanService.BillingPlan - Add detailed comment explaining webpack tree-shaking workaround - Keep import from constants.ts and private static member for webpack reference Co-Authored-By: [email protected] <[email protected]> * fix: Remove Slack reference from comment and restore static usage - Remove Slack URL from explanatory comment - Restore BillingPlanService.BillingPlan usage throughout class methods - This static member usage is essential for webpack to track the enum reference Co-Authored-By: [email protected] <[email protected]> * Fix BillingPlan issue by finding root cause --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
import {
|
|
type BillingPlan,
|
|
BILLING_PLANS,
|
|
ENTERPRISE_SLUGS,
|
|
PLATFORM_ENTERPRISE_SLUGS,
|
|
PLATFORM_PLANS_MAP,
|
|
} from "@calcom/features/ee/billing/constants";
|
|
import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils";
|
|
import type { JsonValue } from "@calcom/types/Json";
|
|
|
|
export class BillingPlanService {
|
|
async getUserPlanByMemberships(
|
|
memberships: {
|
|
team: {
|
|
isOrganization: boolean;
|
|
isPlatform: boolean;
|
|
slug: string | null;
|
|
metadata: JsonValue;
|
|
parent: {
|
|
isOrganization: boolean;
|
|
slug: string | null;
|
|
isPlatform: boolean;
|
|
metadata: JsonValue;
|
|
} | null;
|
|
platformBilling: {
|
|
plan: string;
|
|
} | null;
|
|
};
|
|
user: {
|
|
isPlatformManaged: boolean;
|
|
};
|
|
}[]
|
|
): Promise<BillingPlan> {
|
|
if (memberships.length === 0) return BILLING_PLANS.INDIVIDUALS;
|
|
|
|
for (const { team, user } of memberships) {
|
|
if (team.isPlatform || user.isPlatformManaged) {
|
|
if (PLATFORM_ENTERPRISE_SLUGS.includes(team.slug ?? "")) return BILLING_PLANS.PLATFORM_ENTERPRISE;
|
|
if (!team.platformBilling) continue;
|
|
|
|
return PLATFORM_PLANS_MAP[team.platformBilling.plan] ?? team.platformBilling.plan;
|
|
}
|
|
const parentTeamMetadataResult = teamMetadataStrictSchema.safeParse(team.parent?.metadata ?? {});
|
|
const parentTeamMetadata = parentTeamMetadataResult.success ? parentTeamMetadataResult.data : null;
|
|
if (
|
|
team.parent &&
|
|
team.parent.isOrganization &&
|
|
parentTeamMetadata?.subscriptionId &&
|
|
!team.parent.isPlatform
|
|
) {
|
|
return ENTERPRISE_SLUGS.includes(team.parent.slug ?? "")
|
|
? BILLING_PLANS.ENTERPRISE
|
|
: BILLING_PLANS.ORGANIZATIONS;
|
|
}
|
|
const teamMetadataResult = teamMetadataStrictSchema.safeParse(team.metadata ?? {});
|
|
const teamMetadata = teamMetadataResult.success ? teamMetadataResult.data : null;
|
|
// (emrysal) if we do an early return on !teamMetadata?.subscriptionId here, the bundler is not smart enough to infer
|
|
// that it shouldn't clear out the BILLING_PLANS before the for() scope finishes.
|
|
if (team.isOrganization && teamMetadata?.subscriptionId) {
|
|
return ENTERPRISE_SLUGS.includes(team.slug ?? "")
|
|
? BILLING_PLANS.ENTERPRISE
|
|
: BILLING_PLANS.ORGANIZATIONS;
|
|
}
|
|
if (teamMetadata?.subscriptionId) {
|
|
return BILLING_PLANS.TEAMS;
|
|
}
|
|
// no subscriptionId or parent subscription id in this loop, so this membership hasn't got a plan.
|
|
// continue;
|
|
}
|
|
return BILLING_PLANS.UNKNOWN;
|
|
}
|
|
}
|