Skip to content

Conversation

@emrysal
Copy link
Contributor

@emrysal emrysal commented Oct 2, 2025

What does this PR do?

Fixes a critical runtime error where BillingPlan enum was being tree-shaken by webpack despite being properly imported and used, causing TRPCError: BillingPlan is not defined in the hasTeamPlan tRPC endpoint.

Thread: https://calendso.slack.com/archives/C08LT9BLEET/p1759420015428149
Requested by: @emrysal
Link to Devin run: https://app.devin.ai/sessions/6e14b204c786437f94e25ae8e99221e0

How should this be tested?

  1. Runtime test: Trigger the hasTeamPlan tRPC endpoint (occurs when accessing team-related features)
  2. Expected behavior: No TRPCError: BillingPlan is not defined should occur

Human Review Checklist

  • Test runtime behavior: Confirm the original error is resolved in the hasTeamPlan endpoint

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.

Note: This is a webpack bundling issue that's difficult to unit test. The fix effectiveness can only be verified through runtime testing of the tRPC endpoint.

devin-ai-integration bot and others added 4 commits October 2, 2025 16:19
…-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]>
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]>
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]>
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]>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

Walkthrough

Replaces the exported BillingPlan enum with a string-literal map BILLING_PLANS and derives a BillingPlan type from it. Updates getUserPlanByMemberships to explicitly return Promise<BillingPlan> and to use BILLING_PLANS.* values for all comparisons and return paths. Refactors membership metadata parsing to use safe-parse results and branches to determine PLATFORM_ENTERPRISE, ENTERPRISE, ORGANIZATIONS, TEAMS, or UNKNOWN. Adds ENTERPRISE_SLUGS and updates PLATFORM_PLANS_MAP to reference BILLING_PLANS.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title claims to fix a BillingPlan tree-shaking runtime error, but the actual code changes migrate from the BillingPlan enum to a BILLING_PLANS map and adjust plan-determination logic, so the title does not accurately summarize the main change. Revise the title to reflect the primary refactoring—switching from the BillingPlan enum to the BILLING_PLANS constant and updating getUserPlanByMemberships logic—so it clearly matches the code changes.
Description Check ⚠️ Warning The pull request description centers on adding a private static BillingPlan reference in BillingPlanService to prevent enum tree-shaking, but the actual changes refactor getUserPlanByMemberships and constants.ts to use a BILLING_PLANS map with safe parsing logic, with no edits to BillingPlanService or static properties. The description does not match the provided changeset. Revise the pull request description so it accurately reflects the modifications to getUserPlanByMemberships and the BILLING_PLANS refactor in constants.ts, or adjust the code to implement the described BillingPlanService static reference fix.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1759420183-fix-billingplan-import

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@keithwillcode keithwillcode added core area: core, team members only foundation labels Oct 2, 2025
@emrysal emrysal requested a review from hariombalhara October 2, 2025 16:56
- 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]>
@pull-request-size pull-request-size bot added size/XS and removed size/S labels Oct 2, 2025
@vercel
Copy link

vercel bot commented Oct 2, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Oct 2, 2025 11:24pm
cal-eu Ignored Ignored Oct 2, 2025 11:24pm

- 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]>
@pull-request-size pull-request-size bot added size/S and removed size/XS labels Oct 2, 2025
@emrysal emrysal marked this pull request as ready for review October 2, 2025 17:10
@graphite-app graphite-app bot requested a review from a team October 2, 2025 17:10
@dosubot dosubot bot added the billing area: billing, stripe, payments, paypal, get paid label Oct 2, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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.

📥 Commits

Reviewing files that changed from the base of the PR and between a1ac5f3 and 05939e5.

📒 Files selected for processing (1)
  • packages/features/ee/billing/domain/billing-plans.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/features/ee/billing/domain/billing-plans.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/ee/billing/domain/billing-plans.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/features/ee/billing/domain/billing-plans.ts
🧬 Code graph analysis (1)
packages/features/ee/billing/domain/billing-plans.ts (1)
packages/features/ee/billing/constants.ts (2)
  • PLATFORM_ENTERPRISE_SLUGS (31-31)
  • ENTERPRISE_SLUGS (32-32)
🔇 Additional comments (1)
packages/features/ee/billing/domain/billing-plans.ts (1)

39-84: No direct BillingPlan imports found
Ripgrep confirms no files import the BillingPlan enum from constants.ts; all usages go through BillingPlanService.

@pull-request-size pull-request-size bot added size/L and removed size/S labels Oct 2, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
packages/features/ee/billing/domain/billing-plans.ts (1)

55-69: Remove outdated bundler comment and consider simplification.

Lines 57-58 contain a comment about bundler intelligence that appears outdated. Since the PR switched from enum to const object (which solves the tree-shaking issue), this comment about early returns affecting BILLING_PLANS clearing is no longer relevant.

Apply this diff to remove the outdated comment:

-      // (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) {

Additionally, the control flow could be simplified by using early returns:

       const teamMetadataResult = teamMetadataStrictSchema.safeParse(team.metadata ?? {});
       const teamMetadata = teamMetadataResult.success ? teamMetadataResult.data : null;
+      
+      if (!teamMetadata?.subscriptionId) continue;
+      
       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.TEAMS;
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 05939e5 and fdc911f.

📒 Files selected for processing (2)
  • packages/features/ee/billing/constants.ts (1 hunks)
  • packages/features/ee/billing/domain/billing-plans.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/features/ee/billing/constants.ts
  • packages/features/ee/billing/domain/billing-plans.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/ee/billing/constants.ts
  • packages/features/ee/billing/domain/billing-plans.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/features/ee/billing/constants.ts
  • packages/features/ee/billing/domain/billing-plans.ts
🧬 Code graph analysis (1)
packages/features/ee/billing/domain/billing-plans.ts (2)
packages/features/ee/billing/constants.ts (5)
  • BillingPlan (23-23)
  • BILLING_PLANS (11-21)
  • PLATFORM_ENTERPRISE_SLUGS (33-33)
  • PLATFORM_PLANS_MAP (25-31)
  • ENTERPRISE_SLUGS (34-34)
packages/prisma/zod-utils.ts (1)
  • teamMetadataStrictSchema (389-405)
🔇 Additional comments (8)
packages/features/ee/billing/constants.ts (3)

23-23: LGTM! Type-safe approach for billing plans.

The BillingPlan type derived from BILLING_PLANS is idiomatic and provides better tree-shaking than the previous enum approach. This addresses the root cause mentioned in the PR objectives.


25-31: LGTM! Map correctly updated for new structure.

The PLATFORM_PLANS_MAP has been correctly updated to reference the new BILLING_PLANS constants, maintaining type safety with Record<string, BillingPlan>.


34-34: LGTM! Safe environment variable handling.

The ENTERPRISE_SLUGS constant follows the same safe pattern as PLATFORM_ENTERPRISE_SLUGS, with appropriate fallback to an empty array when the environment variable is not set.

packages/features/ee/billing/domain/billing-plans.ts (5)

1-33: LGTM! Imports and return type correctly updated.

The imports and return type have been correctly updated to use the new BILLING_PLANS constant and BillingPlan type, aligning with the improved tree-shaking approach.


43-54: LGTM! Parent organization logic is correct.

The safeParse approach for parent metadata is safe and the logic correctly identifies enterprise vs. organizations plans based on ENTERPRISE_SLUGS.


70-70: LGTM! Appropriate fallback for unknown plans.

The final return of BILLING_PLANS.UNKNOWN is correct and provides a safe default when no billing plan is determined from memberships.


1-72: Previous review concerns addressed by const object approach.

The current implementation uses the const object + type union approach (Option 2 from the previous review), which is one of the recommended solutions. This eliminates the need for the private static BillingPlan workaround and provides better tree-shaking without coupling the enum to the service class.

Based on past review comments.


36-42: Ensure type-safe fallback for platform billing plans

Line 41 may return a raw string outside the BillingPlan enum. Replace the fallback with an explicit value or cast:

  • return PLATFORM_PLANS_MAP[team.platformBilling.plan] ?? BILLING_PLANS.UNKNOWN;
  • or return PLATFORM_PLANS_MAP[team.platformBilling.plan] ?? (team.platformBilling.plan as BillingPlan);

Verify that PLATFORM_PLANS_MAP includes all possible team.platformBilling.plan values.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 3, 2025

E2E results are ready!

@emrysal emrysal merged commit 409a027 into main Oct 3, 2025
82 of 85 checks passed
@emrysal emrysal deleted the devin/1759420183-fix-billingplan-import branch October 3, 2025 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

billing area: billing, stripe, payments, paypal, get paid core area: core, team members only foundation ready-for-e2e size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants