Skip to content

Comments

perf: move to disable prisma client extension inference#23692

Merged
emrysal merged 10 commits intomainfrom
perf/prisma-disable-inference-client-extensions
Sep 9, 2025
Merged

perf: move to disable prisma client extension inference#23692
emrysal merged 10 commits intomainfrom
perf/prisma-disable-inference-client-extensions

Conversation

@emrysal
Copy link
Contributor

@emrysal emrysal commented Sep 8, 2025

What does this PR do?

Spin-off PR from the Prisma 6.7.0 migration work, we discovered a major bottleneck in the typings of the Prisma client extensions, as well as making dependency injection harder as a typed client extension prisma cannot be injected on a client extended prisma with different extensions.

It looks like each client extension definition duplicates the type definitions, so e.g. 6 extensions, duplicate typings of our entire PrismaClient 6x.

Also removes the withAccelerate Client Extensions

@emrysal emrysal requested a review from a team as a code owner September 8, 2025 23:50
@emrysal emrysal marked this pull request as draft September 8, 2025 23:50
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

Walkthrough

The PR standardizes Prisma usage and JSON typing across the repo: switches default Prisma imports to named prisma and adds type-only Prisma/PrismaClient imports; casts JSON fields to Prisma.InputJsonValue where stored/updated; consolidates Prisma client construction and extensions in packages/prisma/index.ts (single baseClient → extended prisma export); updates service/repository typings to PrismaClient; introduces a FormResponse type in booking repository; modifies tests to use named imports/enums and relax mock typings; and reworks route/field shapes and new queryValue/attributes fields in routingFormHelpers. Changes are typing/import/shape-focused with no behavioral control-flow changes.

Possibly related PRs

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly identifies the primary change—disabling Prisma client extension inference—and aligns with the PR’s core objective of reducing type duplication and removing the withAccelerate extension.
Description Check ✅ Passed The description provides a clear overview of the PR’s motivation and actions by detailing the bottleneck in Prisma extension typings, the need to disable inference, and the removal of the withAccelerate extension, directly matching the changeset.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch perf/prisma-disable-inference-client-extensions

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 Sep 8, 2025
@dosubot dosubot bot added the 🧹 Improvements Improvements to existing features. Mostly UX/UI label Sep 8, 2025
@pull-request-size pull-request-size bot removed the size/M label Sep 9, 2025
@vercel
Copy link

vercel bot commented Sep 9, 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 Sep 9, 2025 10:36am
cal-eu Ignored Ignored Sep 9, 2025 10:36am

@pull-request-size pull-request-size bot added size/XL and removed size/L labels Sep 9, 2025
@emrysal emrysal marked this pull request as ready for review September 9, 2025 02:16
@emrysal emrysal requested a review from a team September 9, 2025 02:16
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (16)
packages/lib/server/repository/delegationCredential.ts (2)

230-263: Use select (never include) to comply with Prisma query guidelines

Switch this query to explicit select and reuse the existing safe select to avoid overfetching.

-    const delegationCredentials = await prisma.delegationCredential.findMany({
-      where:
-        { enabled: true },
-      include: {
-        workspacePlatform: { select: { slug: true } },
-        organization: {
-          include: {
-            members: {
-              select: {
-                id: true,
-                userId: true,
-                user: { select: { id: true, email: true } },
-                accepted: true,
-              },
-              where: { accepted: true },
-            },
-          },
-        },
-      },
-    });
+    const delegationCredentials = await prisma.delegationCredential.findMany({
+      where: { enabled: true },
+      select: {
+        ...delegationCredentialSafeSelect,
+        organization: {
+          select: {
+            members: {
+              select: {
+                id: true,
+                userId: true,
+                user: { select: { id: true, email: true } },
+                accepted: true,
+              },
+              where: { accepted: true },
+            },
+          },
+        },
+      },
+    });

284-316: Replace include with select in delegationCredential.findMany
Use the existing delegationCredentialSafeSelect for top-level fields and add an explicit organization.select to limit the payload:

 const delegationCredentials = await prisma.delegationCredential.findMany({
   where: { enabled: false },
-  include: {
-    workspacePlatform: { select: { slug: true } },
-    organization: {
-      include: {
-        members: {
-          select: {
-            id: true,
-            userId: true,
-            user: { select: { id: true, email: true } },
-            accepted: true,
-          },
-          where: { accepted: true },
-        },
-      },
-    },
-  },
+  select: {
+    ...delegationCredentialSafeSelect,
+    organization: {
+      select: {
+        members: {
+          select: {
+            id: true,
+            userId: true,
+            user: { select: { id: true, email: true } },
+            accepted: true,
+          },
+          where: { accepted: true },
+        },
+      },
+    },
+  },
 });

Also, two other Prisma calls still use include in:

  • apps/web/app/api/auth/two-factor/totp/setup/route.ts
  • apps/web/app/api/auth/two-factor/totp/disable/route.ts

Convert those to explicit select as well to comply with the repository’s “no include” rule.

packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts (1)

75-83: Fix impossible status-range condition.

(response.status < 200 && response.status >= 300) is always false. Either rely on !response.ok or correct the range check.

-        if (!response.ok || (response.status < 200 && response.status >= 300)) {
+        if (!response.ok || (response.status < 200 || response.status >= 300)) {

Also applies to: 88-96

packages/lib/server/repository/membership.ts (4)

166-221: Replace deep include chains with select to reduce payload and follow guidelines.

Per repo guidance, avoid include. Convert to select at each level.

-    return await prisma.membership.findMany({
-      where: prismaWhere,
-      include: {
-        team: {
-          include: {
+    return await prisma.membership.findMany({
+      where: prismaWhere,
+      select: {
+        team: {
+          select: {
             members: {
               select: membershipSelect,
             },
             parent: {
               select: teamParentSelect,
             },
             eventTypes: {
-              select: {
+              select: {
                 ...eventTypeSelect,
                 hashedLink: true,
                 users: { select: userSelect },
                 children: {
-                  include: {
-                    users: { select: userSelect },
-                  },
+                  select: { users: { select: userSelect } },
                 },
                 hosts: {
-                  include: {
-                    user: { select: userSelect },
-                  },
+                  select: { user: { select: userSelect } },
                 },
                 team: {
-                  select: {
+                  select: {
                     id: true,
                     members: {
-                      select: {
-                        user: {
-                          select: {
-                            timeZone: true,
-                          },
-                        },
-                      },
+                      select: { user: { select: { timeZone: true } } },
                       take: 1,
                     },
                   },
                 },
               },
               orderBy: [
                 { position: "desc" },
                 { id: "asc" },
               ],
             },
           },
         },
       },
     });

292-304: Use select instead of include for team.

-    return await prisma.membership.findMany({
-      where: prismaWhere,
-      include: {
-        team: {
-          include: {
-            parent: { select: teamParentSelect },
-          },
-        },
-      },
-    });
+    return await prisma.membership.findMany({
+      where: prismaWhere,
+      select: {
+        team: { select: { parent: { select: teamParentSelect } } },
+      },
+    });

317-330: Switch top-level include to select.

-    const memberships = await prisma.membership.findMany({
-      where: { teamId },
-      include: {
-        user: {
-          select: {
-            credentials: { select: credentialForCalendarServiceSelect },
-            ...availabilityUserSelect,
-          },
-        },
-      },
-    });
+    const memberships = await prisma.membership.findMany({
+      where: { teamId },
+      select: {
+        user: {
+          select: {
+            credentials: { select: credentialForCalendarServiceSelect },
+            ...availabilityUserSelect,
+          },
+        },
+      },
+    });

429-445: Use select for user fields in recent-members query.

-    return prisma.membership.findMany({
+    return prisma.membership.findMany({
       where: {
         teamId: organizationId,
         createdAt: { gt: time },
         accepted: true,
       },
-      include: {
-        user: {
-          select: { email: true, name: true, id: true },
-        },
-      },
+      select: {
+        user: { select: { email: true, name: true, id: true } },
+      },
     });
packages/prisma/extensions/usage-tracking.ts (1)

18-35: Rename usageTrackingExtention to usageTrackingExtension

  • In packages/prisma/extensions/usage-tracking.ts, update the export signature.
  • In packages/prisma/index.ts, update the import at line 8 and the two calls at lines 43 and 59.
packages/features/insights/server/trpc-router.ts (4)

672-683: Replace include with select to comply with our Prisma guideline (no include).

Using include inflates payloads. Select only what’s needed from the relation.

-      include: {
-        team: {
-          select: {
-            id: true,
-            name: true,
-            logoUrl: true,
-            slug: true,
-            metadata: true,
-          },
-        },
-      },
+      select: {
+        team: {
+          select: {
+            id: true,
+            name: true,
+            logoUrl: true,
+            slug: true,
+            metadata: true,
+          },
+        },
+      },

706-720: Use select instead of include when fetching users.

Keep responses minimal per repo guideline.

-        include: {
-          user: {
-            select: userSelect,
-          },
-        },
+        select: {
+          user: {
+            select: userSelect,
+          },
+        },

733-738: Use select over include for membership lookup.

We only need the user fields for the caller.

-    include: {
-      user: {
-        select: userSelect,
-      },
-    },
+    select: {
+      user: {
+        select: userSelect,
+      },
+    },

753-759: Use select over include when listing users in a team.

-    include: {
-      user: {
-        select: userSelect,
-      },
-    },
+    select: {
+      user: {
+        select: userSelect,
+      },
+    },
packages/lib/server/repository/booking.ts (4)

220-233: Select only attendee.email to reduce payload.

attendees: true returns full rows; you only need email for filtering/removal.

-      attendees: true,
+      attendees: {
+        select: {
+          email: true,
+        },
+      },

342-350: Narrow relation selection and avoid selecting entire JSON blobs by default.

Only chosenRouteId and response are used; also narrow attendees to email only.

-      select: {
-        id: true,
-        attendees: true,
-        userId: true,
-        createdAt: true,
-        status: true,
-        startTime: true,
-        routedFromRoutingFormReponse: true,
-      },
+      select: {
+        id: true,
+        attendees: { select: { email: true } },
+        userId: true,
+        createdAt: true,
+        status: true,
+        startTime: true,
+        routedFromRoutingFormReponse: {
+          select: {
+            chosenRouteId: true,
+            response: true,
+          },
+        },
+      },

358-377: Fix unsafe access and order-sensitive array comparison in routing-form filtering.

  • Potential runtime crash if response or field is missing.
  • Array equality currently depends on order; use set equality.
-    if (virtualQueuesData) {
-      queueBookings = allBookings.filter((booking) => {
-        const responses = booking.routedFromRoutingFormReponse;
-        const fieldId = virtualQueuesData.fieldOptionData.fieldId;
-        const selectedOptionIds = virtualQueuesData.fieldOptionData.selectedOptionIds;
-
-        const response = responses?.response as FormResponse;
-
-        const responseValue = response[fieldId].value;
-
-        if (Array.isArray(responseValue) && Array.isArray(selectedOptionIds)) {
-          //check if all values are the same (this only support 'all in' not 'any in')
-          return (
-            responseValue.length === selectedOptionIds.length &&
-            responseValue.every((value, index) => value === selectedOptionIds[index])
-          );
-        } else {
-          return responseValue === selectedOptionIds;
-        }
-      });
-    }
+    if (virtualQueuesData) {
+      queueBookings = allBookings.filter((booking) => {
+        const responses = booking.routedFromRoutingFormReponse;
+        const { fieldId, selectedOptionIds } = virtualQueuesData.fieldOptionData;
+        const response = (responses?.response as FormResponse | undefined) || undefined;
+        const field = response?.[fieldId];
+        if (!field) return false;
+        const responseValue = field.value;
+
+        if (Array.isArray(responseValue)) {
+          const selected = Array.isArray(selectedOptionIds) ? selectedOptionIds : [selectedOptionIds];
+          const a = new Set(responseValue.map(String));
+          const b = new Set(selected.map(String));
+          if (a.size !== b.size) return false;
+          for (const v of a) if (!b.has(v)) return false;
+          return true;
+        }
+        return String(responseValue) === String(selectedOptionIds);
+      });
+    }

455-460: Avoid selecting user.credentials with true; never risk exposing credential.key.

Reuse credentialForCalendarServiceSelect to whitelist fields.

-        user: {
-          select: {
-            credentials: true,
-          },
-        },
+        user: {
+          select: {
+            credentials: { select: credentialForCalendarServiceSelect },
+          },
+        },
🧹 Nitpick comments (29)
apps/web/test/lib/plainTiers.test.ts (1)

79-81: Plan Prisma 6.7 upgrade to migrate ts-expect-error mocks to mockDeep
Suppressions remain in several tests under apps/api and apps/web. Once we upgrade to Prisma 6.7.0, replace these ad-hoc ts-expect-error mocks with a shared DeepMocked (via mockDeep) and remove the suppressions to keep tests type-safe and avoid drift.

apps/api/v1/test/lib/event-types/[id]/_get.test.ts (1)

87-93: Queue removal of ts-expect-error after Prisma 6.7 mockDeep upgrade
Suppressing the type error is acceptable now; once Prisma 6.7 introduces mockDeep, replace these with typed deep mocks.
Occurrences:

  • apps/api/v1/test/lib/event-types/[id]/_get.test.ts:87
  • apps/api/v1/test/lib/event-types/[id]/_delete.test.ts:53
apps/api/v1/test/lib/attendees/_post.test.ts (1)

80-81: Prefer a typed shape over any for mocked booking

Use a minimal typed shape to guard against accidental shape drift while keeping the mock flexible.

+import type { Booking } from "@prisma/client";
@@
-      const userBooking = { id: 1 };
+      const userBooking = { id: 1 } satisfies Partial<Booking>;
-      prismaMock.booking.findFirst.mockResolvedValue(userBooking as any);
+      prismaMock.booking.findFirst.mockResolvedValue(userBooking as any); // keep until mockDeep is in
packages/lib/server/repository/delegationCredential.ts (1)

100-102: Avoid unknown as Prisma.InputJsonValue; make encryption output typed

Reduce casts by returning Prisma.InputJsonValue from the encryption util (or assert with a narrow guard).

Apply one of:

  • Preferred (in serviceAccountKey module outside this file):
// serviceAccountKey.ts
import type { Prisma } from "@prisma/client";
export type EncryptedServiceAccountKey = Prisma.InputJsonValue;
export function encryptServiceAccountKey(/* ... */): EncryptedServiceAccountKey { /* ... */ }
  • Local guard (keeps this file self-contained):
-        // z.passthrough() is not allowed in Prisma, but we know this is trusted.
-        serviceAccountKey: encryptedKey as unknown as Prisma.InputJsonValue,
+        // encryption returns a JSON-serializable object
+        serviceAccountKey: encryptedKey as Prisma.InputJsonValue,

If encryption can yield non-object JSON (edge), add a runtime assert to ensure it’s JSON-serializable before write.

apps/api/v1/test/lib/event-types/[id]/_delete.test.ts (1)

53-58: Temporary ts-expect-error is acceptable; plan to remove with typed deep mocks

Keep a follow-up to migrate this to a typed mock and delete the suppression.

apps/api/v1/test/lib/bookings/[id]/_delete.test.ts (1)

23-29: Minor: prefer a typed mock to as any

Non-blocking. Consider satisfies Partial<User> (from @prisma/client) to keep shape-checked while you wait for mockDeep.

apps/api/v1/test/lib/bookings/[id]/_patch.test.ts (1)

18-24: Same note: avoid any in mocks where feasible

Use a minimal typed shape (satisfies Partial<User>) and drop the any once mockDeep arrives.

packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts (1)

100-109: Cast to Prisma.InputJsonValue is fine; consider validating JSON-serializability.

The double-cast is acceptable, but a small helper to assert serializable JSON would prevent accidental Dates/Maps from slipping in.

packages/lib/server/repository/membership.ts (1)

488-490: Prefer enum values over string literals.

Use MembershipRole.ADMIN and MembershipRole.OWNER for consistency and type safety.

-        role: {
-          in: ["ADMIN", "OWNER"],
-        },
+        role: { in: [MembershipRole.ADMIN, MembershipRole.OWNER] },
packages/app-store/_utils/oauth/updateTokenObject.ts (1)

21-28: Unify casts: drop the intermediate unknown.

Other code in this file casts directly to Prisma.InputJsonValue. Mirror that here.

-    data: {
-      key: tokenObject as unknown as Prisma.InputJsonValue,
-    },
+    data: { key: tokenObject as Prisma.InputJsonValue },
packages/prisma/extensions/usage-tracking.ts (1)

2-2: Consider using the unified Prisma type import path.

For consistency with the rest of the repo, import PrismaClient from @calcom/prisma/client.

apps/api/v1/test/lib/bookings/_get.test.ts (3)

28-34: Type loosening in mocks is acceptable as a stopgap.

Track a follow-up to remove as any once the Prisma 6.7.0 mock upgrade lands.


37-45: ts-expect-error is fine here; keep it temporary.

Please add a TODO with the PR/issue reference for removal post-migration.


47-53: Same here: minimize as any usage in mocks over time.

packages/features/insights/server/trpc-router.ts (3)

209-211: Avoid fetching entire membership rows; select only id.

We only check existence; cut unused columns.

-  const membership = await ctx.insightsDb.membership.findFirst({
-    where: membershipWhereConditional,
-  });
+  const membership = await ctx.insightsDb.membership.findFirst({
+    where: membershipWhereConditional,
+    select: { id: true },
+  });

220-225: Select only id for isChildTeamOfOrg check.

-      const isChildTeamOfOrg = await ctx.insightsDb.team.findFirst({
-        where: {
-          id: parse.data.teamId,
-          parentId: ctx.user.organizationId,
-        },
-      });
+      const isChildTeamOfOrg = await ctx.insightsDb.team.findFirst({
+        where: {
+          id: parse.data.teamId,
+          parentId: ctx.user.organizationId,
+        },
+        select: { id: true },
+      });

231-240: Select only id when verifying org membership role.

-    const membershipOrg = await ctx.insightsDb.membership.findFirst({
+    const membershipOrg = await ctx.insightsDb.membership.findFirst({
       where: {
         userId: ctx.user.id,
         teamId: ctx.user.organizationId,
         accepted: true,
         role: {
           in: ["OWNER", "ADMIN"],
         },
       },
-    });
+      select: { id: true },
+    });
packages/app-store/zoomvideo/lib/VideoApiAdapter.ts (1)

428-429: Prefer named exports over default exports.

Improves tree-shaking and refactors.

-export default ZoomVideoApiAdapter;
+export { ZoomVideoApiAdapter };
apps/api/v1/pages/api/credential-sync/_patch.ts (1)

6-8: LGTM; minimal select avoids leaking credential.key.

Casting key to Prisma.InputJsonValue is fine. Consider centralizing this cast in a small helper to DRY across endpoints.

Also applies to: 74-75

packages/lib/server/repository/booking.ts (1)

616-658: Consider returning minimal fields (or bookingMinimalSelect) for team booking queries.

findMany without select returns full Booking rows; prefer bookingMinimalSelect or explicit selects for perf.

Would you like me to propose concrete selects for these calls based on their current consumers?

Also applies to: 793-814

apps/api/v1/test/lib/bookings/_post.test.ts (1)

473-476: Use BookingStatus enum instead of string literal for consistency.

-        prismaMock.booking.findUnique.mockResolvedValue({
-          ...originalBooking,
-          status: "CANCELLED",
-        });
+        prismaMock.booking.findUnique.mockResolvedValue({
+          ...originalBooking,
+          status: BookingStatus.CANCELLED,
+        });

Also applies to: 540-541

apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (1)

8-8: Remove unused Prisma import.

Keeps helpers lean.

-import { Prisma } from "@calcom/prisma/client";
packages/prisma/index.ts (7)

13-15: Make global typing accurate (optional)
baseClient can be undefined on first load; type it as optional and prefer globalThis for portability.

Apply this diff:

-const globalForPrisma = global as unknown as {
-  baseClient: PrismaClient;
-};
+const globalForPrisma = globalThis as unknown as {
+  baseClient?: PrismaClient;
+};

38-40: Nit: prefer nullish coalescing for readability
Equivalent behavior, slightly clearer.

-const baseClient =
-  globalForPrisma.baseClient || new PrismaClient(prismaOptions);
+const baseClient =
+  globalForPrisma.baseClient ?? new PrismaClient(prismaOptions);

41-48: DRY the extension pipeline and centralize the cast
The $extends chain and the as unknown as PrismaClient cast are duplicated. Extract a helper to avoid drift and keep the “disable inference” cast in one place.

+const applyExtensions = (c: PrismaClient): PrismaClient =>
+  (c
+    .$extends(usageTrackingExtention(baseClient))
+    .$extends(excludeLockedUsersExtension())
+    .$extends(excludePendingPaymentsExtension())
+    .$extends(bookingIdempotencyKeyExtension())
+    .$extends(disallowUndefinedDeleteUpdateManyExtension())) as unknown as PrismaClient;
+
-export const customPrisma = (options?: Prisma.PrismaClientOptions) =>
-  new PrismaClient({ ...prismaOptions, ...options })
-    .$extends(usageTrackingExtention(baseClient))
-    .$extends(excludeLockedUsersExtension())
-    .$extends(excludePendingPaymentsExtension())
-    .$extends(bookingIdempotencyKeyExtension())
-    .$extends(disallowUndefinedDeleteUpdateManyExtension()) as unknown as PrismaClient;
+export const customPrisma = (options?: Prisma.PrismaClientOptions) =>
+  applyExtensions(new PrismaClient({ ...prismaOptions, ...options }));
 
-export const prisma: PrismaClient = baseClient
-  .$extends(usageTrackingExtention(baseClient))
-  .$extends(excludeLockedUsersExtension())
-  .$extends(excludePendingPaymentsExtension())
-  .$extends(bookingIdempotencyKeyExtension())
-  .$extends(disallowUndefinedDeleteUpdateManyExtension()) as unknown as PrismaClient;
+export const prisma: PrismaClient = applyExtensions(baseClient);

Also applies to: 58-64


41-44: Confirm usage tracking on secondary clients
customPrisma also gets usageTrackingExtention(baseClient). If customPrisma points to INSIGHTS/readonly DB, accidental writes would still record usage against the primary DB, even if the write fails. Confirm this is intended; otherwise consider disabling usage tracking for readonlyPrisma.


51-51: Migrate bookingReferenceMiddleware to $extends
Align with the extension pipeline for consistent ordering and easier typing.


83-87: Prefer Prisma.TransactionClient over Omit<> shim
Use the official transaction client type to avoid drift and improve accuracy.

Apply this diff:

-export type { 
-  OmitPrismaClient as PrismaTransaction, 
-  // we re-export the native PrismaClient type for backwards-compatibility.
-  PrismaClient
-};
+// we re-export the native PrismaClient type for backwards-compatibility.
+export type { PrismaClient };
+export type PrismaTransaction = Prisma.TransactionClient;

Additionally remove the now-unused alias:

-type OmitPrismaClient = Omit<
-  PrismaClient,
-  "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends"
->;

92-92: Avoid default export
Our guidelines prefer named exports. Consider dropping the default export and updating imports.

If you want, I can generate a repo-wide search to list default-import call sites for @calcom/prisma.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • 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 300cd31 and b0c4057.

📒 Files selected for processing (25)
  • apps/api/v1/pages/api/credential-sync/_patch.ts (2 hunks)
  • apps/api/v1/pages/api/credential-sync/_post.ts (2 hunks)
  • apps/api/v1/test/lib/attendees/_post.test.ts (1 hunks)
  • apps/api/v1/test/lib/bookings/[id]/_delete.test.ts (1 hunks)
  • apps/api/v1/test/lib/bookings/[id]/_patch.test.ts (1 hunks)
  • apps/api/v1/test/lib/bookings/_get.test.ts (2 hunks)
  • apps/api/v1/test/lib/bookings/_post.test.ts (11 hunks)
  • apps/api/v1/test/lib/event-types/[id]/_delete.test.ts (1 hunks)
  • apps/api/v1/test/lib/event-types/[id]/_get.test.ts (1 hunks)
  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (4 hunks)
  • apps/web/test/lib/plainTiers.test.ts (1 hunks)
  • packages/app-store/_utils/oauth/updateTokenObject.ts (2 hunks)
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts (2 hunks)
  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts (2 hunks)
  • packages/features/flags/features.repository.ts (0 hunks)
  • packages/features/insights/server/trpc-router.ts (4 hunks)
  • packages/lib/server/repository/booking.ts (4 hunks)
  • packages/lib/server/repository/delegationCredential.ts (1 hunks)
  • packages/lib/server/repository/membership.ts (1 hunks)
  • packages/lib/server/repository/workspacePlatform.ts (3 hunks)
  • packages/lib/server/service/InsightsBookingBaseService.ts (3 hunks)
  • packages/lib/server/service/InsightsRoutingBaseService.ts (3 hunks)
  • packages/lib/server/service/InsightsRoutingDIService.ts (2 hunks)
  • packages/prisma/extensions/usage-tracking.ts (2 hunks)
  • packages/prisma/index.ts (4 hunks)
💤 Files with no reviewable changes (1)
  • packages/features/flags/features.repository.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.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/lib/server/repository/delegationCredential.ts
  • apps/api/v1/test/lib/attendees/_post.test.ts
  • apps/api/v1/pages/api/credential-sync/_post.ts
  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
  • apps/web/test/lib/plainTiers.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_get.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_delete.test.ts
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts
  • packages/lib/server/service/InsightsRoutingDIService.ts
  • packages/prisma/extensions/usage-tracking.ts
  • apps/api/v1/pages/api/credential-sync/_patch.ts
  • packages/lib/server/service/InsightsRoutingBaseService.ts
  • apps/api/v1/test/lib/bookings/[id]/_delete.test.ts
  • packages/lib/server/service/InsightsBookingBaseService.ts
  • apps/api/v1/test/lib/bookings/[id]/_patch.test.ts
  • packages/app-store/_utils/oauth/updateTokenObject.ts
  • apps/api/v1/test/lib/bookings/_get.test.ts
  • packages/lib/server/repository/workspacePlatform.ts
  • packages/features/insights/server/trpc-router.ts
  • apps/api/v1/test/lib/bookings/_post.test.ts
  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/membership.ts
  • packages/prisma/index.ts
  • apps/web/playwright/lib/test-helpers/routingFormHelpers.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/lib/server/repository/delegationCredential.ts
  • apps/api/v1/test/lib/attendees/_post.test.ts
  • apps/api/v1/pages/api/credential-sync/_post.ts
  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
  • apps/web/test/lib/plainTiers.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_get.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_delete.test.ts
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts
  • packages/lib/server/service/InsightsRoutingDIService.ts
  • packages/prisma/extensions/usage-tracking.ts
  • apps/api/v1/pages/api/credential-sync/_patch.ts
  • packages/lib/server/service/InsightsRoutingBaseService.ts
  • apps/api/v1/test/lib/bookings/[id]/_delete.test.ts
  • packages/lib/server/service/InsightsBookingBaseService.ts
  • apps/api/v1/test/lib/bookings/[id]/_patch.test.ts
  • packages/app-store/_utils/oauth/updateTokenObject.ts
  • apps/api/v1/test/lib/bookings/_get.test.ts
  • packages/lib/server/repository/workspacePlatform.ts
  • packages/features/insights/server/trpc-router.ts
  • apps/api/v1/test/lib/bookings/_post.test.ts
  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/membership.ts
  • packages/prisma/index.ts
  • apps/web/playwright/lib/test-helpers/routingFormHelpers.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/lib/server/repository/delegationCredential.ts
  • apps/api/v1/test/lib/attendees/_post.test.ts
  • apps/api/v1/pages/api/credential-sync/_post.ts
  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
  • apps/web/test/lib/plainTiers.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_get.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_delete.test.ts
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts
  • packages/lib/server/service/InsightsRoutingDIService.ts
  • packages/prisma/extensions/usage-tracking.ts
  • apps/api/v1/pages/api/credential-sync/_patch.ts
  • packages/lib/server/service/InsightsRoutingBaseService.ts
  • apps/api/v1/test/lib/bookings/[id]/_delete.test.ts
  • packages/lib/server/service/InsightsBookingBaseService.ts
  • apps/api/v1/test/lib/bookings/[id]/_patch.test.ts
  • packages/app-store/_utils/oauth/updateTokenObject.ts
  • apps/api/v1/test/lib/bookings/_get.test.ts
  • packages/lib/server/repository/workspacePlatform.ts
  • packages/features/insights/server/trpc-router.ts
  • apps/api/v1/test/lib/bookings/_post.test.ts
  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/membership.ts
  • packages/prisma/index.ts
  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts
**/*Service.ts

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

Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Files:

  • packages/lib/server/service/InsightsRoutingDIService.ts
  • packages/lib/server/service/InsightsRoutingBaseService.ts
  • packages/lib/server/service/InsightsBookingBaseService.ts
🧠 Learnings (18)
📓 Common learnings
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
Learnt from: hariombalhara
PR: calcom/cal.com#23541
File: packages/features/bookings/lib/di/modules/RegularBookingServiceModule.ts:22-28
Timestamp: 2025-09-03T09:52:51.182Z
Learning: The IBookingServiceDependencies interface in packages/features/bookings/lib/handleNewBooking.ts contains 6 properties: cacheService, checkBookingAndDurationLimitsService, prismaClient, bookingRepository, featuresRepository, and checkBookingLimitsService. This interface is used by RegularBookingService and matches the depsMap structure in RegularBookingServiceModule.
📚 Learning: 2025-09-01T08:56:14.071Z
Learnt from: nangelina
PR: calcom/cal.com#23486
File: packages/app-store/kyzon-space/lib/tokenManager.ts:25-31
Timestamp: 2025-09-01T08:56:14.071Z
Learning: In token refresh utilities like tokenManager.ts, accessing credential.key from Prisma is legitimate and necessary for OAuth token refresh flows. These internal utilities need stored credentials to refresh tokens and don't expose them in API responses.

Applied to files:

  • packages/lib/server/repository/delegationCredential.ts
  • apps/api/v1/pages/api/credential-sync/_post.ts
  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts
  • apps/api/v1/pages/api/credential-sync/_patch.ts
  • packages/app-store/_utils/oauth/updateTokenObject.ts
  • packages/lib/server/repository/workspacePlatform.ts
📚 Learning: 2025-07-28T11:50:23.946Z
Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*.ts : Ensure the `credential.key` field is never returned from tRPC endpoints or APIs

Applied to files:

  • packages/lib/server/repository/delegationCredential.ts
  • apps/api/v1/pages/api/credential-sync/_post.ts
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.

Applied to files:

  • apps/api/v1/test/lib/attendees/_post.test.ts
  • apps/api/v1/test/lib/event-types/[id]/_get.test.ts
  • apps/api/v1/test/lib/bookings/[id]/_delete.test.ts
  • apps/api/v1/test/lib/bookings/[id]/_patch.test.ts
  • apps/api/v1/test/lib/bookings/_get.test.ts
  • apps/api/v1/test/lib/bookings/_post.test.ts
  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts
📚 Learning: 2025-09-01T07:31:00.963Z
Learnt from: nangelina
PR: calcom/cal.com#23486
File: packages/app-store/kyzon-space/config.json:5-5
Timestamp: 2025-09-01T07:31:00.963Z
Learning: In Cal.com's video integration system, credential types (e.g., "kyzon-space_video") are transformed to app names by removing underscores using `cred.type.split("_").join("")` in videoClient.ts line 28. This means the key in packages/app-store/index.ts should match the underscore-removed version (e.g., "kyzon-spacevideo") rather than the original type name.

Applied to files:

  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts
📚 Learning: 2025-09-01T07:31:00.963Z
Learnt from: nangelina
PR: calcom/cal.com#23486
File: packages/app-store/kyzon-space/config.json:5-5
Timestamp: 2025-09-01T07:31:00.963Z
Learning: In Cal.com's video integration system, credential types (e.g., "kyzon-space_video") are transformed to app names by removing underscores using `cred.type.split("_").join("")` in videoClient.ts line 28. This means the key in packages/app-store/index.ts should match the underscore-removed version (e.g., "kyzon-spacevideo") rather than the original type name. This same pattern is used in other parts of the system like getCalendar.ts.

Applied to files:

  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
  • packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts
📚 Learning: 2025-07-24T08:39:06.185Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22702
File: packages/lib/server/service/insightsBooking.ts:120-124
Timestamp: 2025-07-24T08:39:06.185Z
Learning: In the InsightsBookingService (packages/lib/server/service/insightsBooking.ts), the constructor stores null for invalid options or filters but this is handled safely through null checks in buildFilterConditions() and buildAuthorizationConditions() methods. The service uses defensive programming to return safe fallback conditions (null or NOTHING_CONDITION) rather than throwing errors on invalid inputs.

Applied to files:

  • packages/lib/server/service/InsightsBookingBaseService.ts
📚 Learning: 2025-09-03T09:52:51.182Z
Learnt from: hariombalhara
PR: calcom/cal.com#23541
File: packages/features/bookings/lib/di/modules/RegularBookingServiceModule.ts:22-28
Timestamp: 2025-09-03T09:52:51.182Z
Learning: The IBookingServiceDependencies interface in packages/features/bookings/lib/handleNewBooking.ts contains 6 properties: cacheService, checkBookingAndDurationLimitsService, prismaClient, bookingRepository, featuresRepository, and checkBookingLimitsService. This interface is used by RegularBookingService and matches the depsMap structure in RegularBookingServiceModule.

Applied to files:

  • packages/lib/server/service/InsightsBookingBaseService.ts
  • packages/features/insights/server/trpc-router.ts
📚 Learning: 2025-08-26T08:08:23.395Z
Learnt from: SinghaAnirban005
PR: calcom/cal.com#23343
File: packages/features/insights/server/trpc-router.ts:1080-1101
Timestamp: 2025-08-26T08:08:23.395Z
Learning: In packages/features/insights/server/trpc-router.ts, when filtering personal event types (userId provided, no teamId, not isAll), the query correctly uses user.id (authenticated user) instead of the input userId parameter for security reasons. This prevents users from accessing other users' personal event types by passing arbitrary user IDs.

Applied to files:

  • packages/features/insights/server/trpc-router.ts
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking fields are restricted structures containing only specific fields (id, eventTypeId, userId, and sometimes additional fields like startTime or smsReminderNumber) rather than full database booking objects, so there are no security or PII leakage concerns when using these booking objects in webhook payloads.

Applied to files:

  • apps/api/v1/test/lib/bookings/_post.test.ts
📚 Learning: 2025-08-21T13:55:23.470Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/service/FormWebhookService.ts:0-0
Timestamp: 2025-08-21T13:55:23.470Z
Learning: In the new webhook architecture for Cal.com, schedulePayload is considered legacy code that doesn't fit the clean architecture. The new architecture keeps webhook scheduling logic within the Service layer, specifically through a new method WebhookService.scheduleDelayedWebhooks, rather than using the old centralized schedulePayload helper.

Applied to files:

  • apps/api/v1/test/lib/bookings/_post.test.ts
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In BookingPaymentInitiatedDTO and other webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking field is a restricted structure containing only specific fields (id, eventTypeId, userId) rather than the full database booking object, so there are no security or PII leakage concerns when passing the booking object to buildEventPayload.

Applied to files:

  • apps/api/v1/test/lib/bookings/_post.test.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/membership.ts
📚 Learning: 2025-09-08T07:27:42.863Z
Learnt from: vijayraghav-io
PR: calcom/cal.com#16878
File: packages/app-store/feishucalendar/api/callback.ts:72-79
Timestamp: 2025-09-08T07:27:42.863Z
Learning: In the Cal.com codebase, some calendar integrations like google-calendar already use SelectedCalendarRepository.create for selectedCalendar creation, which automatically triggers reconnection logic, while others like feishucalendar use direct prisma.selectedCalendar.create calls that bypass the repository hooks.

Applied to files:

  • packages/lib/server/repository/membership.ts
📚 Learning: 2025-09-08T07:27:42.863Z
Learnt from: vijayraghav-io
PR: calcom/cal.com#16878
File: packages/app-store/feishucalendar/api/callback.ts:72-79
Timestamp: 2025-09-08T07:27:42.863Z
Learning: Four calendar integrations in Cal.com still use direct prisma.selectedCalendar.create instead of SelectedCalendarRepository.create: feishucalendar, zohocalendar, office365calendar, and larkcalendar. These bypass repository hooks and won't trigger reconnection logic for BookingReferences.

Applied to files:

  • packages/lib/server/repository/membership.ts
📚 Learning: 2025-07-15T12:59:34.389Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Applied to files:

  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts
🧬 Code graph analysis (7)
packages/lib/server/service/InsightsRoutingDIService.ts (1)
packages/prisma/index.ts (1)
  • PrismaClient (86-86)
packages/prisma/extensions/usage-tracking.ts (1)
packages/features/ee/common/server/LicenseKeyService.ts (2)
  • incrementUsage (90-105)
  • incrementUsage (127-130)
packages/lib/server/service/InsightsRoutingBaseService.ts (1)
packages/prisma/index.ts (1)
  • PrismaClient (86-86)
packages/lib/server/service/InsightsBookingBaseService.ts (1)
packages/prisma/index.ts (1)
  • PrismaClient (86-86)
packages/features/insights/server/trpc-router.ts (1)
packages/prisma/index.ts (1)
  • PrismaClient (86-86)
packages/prisma/index.ts (1)
packages/prisma/extensions/usage-tracking.ts (1)
  • usageTrackingExtention (18-35)
apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (1)
packages/lib/constants.ts (1)
  • WEBAPP_URL (12-18)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Production builds / Build Web App
  • GitHub Check: Production builds / Build Atoms
  • GitHub Check: Production builds / Build API v2
  • GitHub Check: Production builds / Build Docs
  • GitHub Check: Tests / Unit
  • GitHub Check: Production builds / Build API v1
  • GitHub Check: Linters / lint
  • GitHub Check: Type check / check-types
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (20)
packages/app-store/nextcloudtalk/lib/VideoApiAdapter.ts (1)

8-8: Type-only Prisma import is correct.

Matches the repo-wide pattern and avoids bundling the client at runtime.

apps/api/v1/pages/api/credential-sync/_post.ts (2)

9-11: Prisma import changes look good.

Switch to named prisma and type-only Prisma aligns with the new client setup.


92-99: Safe JSON typing on credential.key.

Casting to Prisma.InputJsonValue is consistent with the new typing strategy. You also avoid returning credential.key in the API response, which satisfies the security guideline.

packages/lib/server/repository/membership.ts (3)

1-1: Good: unified prisma import with transaction type.


3-3: Type-only import cleanup is correct.


240-276: Nice: this path already uses select.

Keeping select here is aligned with the guideline.

packages/app-store/_utils/oauth/updateTokenObject.ts (1)

5-7: Prisma import updates are consistent with the new client.

packages/lib/server/service/InsightsRoutingDIService.ts (2)

1-1: Correct: interface now depends on PrismaClient type.


10-11: LGTM.

Public contract now uses PrismaClient; construction remains unchanged.

packages/lib/server/repository/workspacePlatform.ts (2)

2-2: Good: use @calcom/prisma/client types.


42-48: Create: correct handling of null vs undefined JSON.

Casting to InputJsonValue with Prisma.JsonNull fallback is correct.

packages/prisma/extensions/usage-tracking.ts (1)

8-9: Signature change to PrismaClient is correct.

Works with extension inference disabled; dependency injection remains clear.

packages/features/insights/server/trpc-router.ts (1)

772-788: No issue: ctx.prisma is exposed in this router
createContextInner returns both prisma and insightsDb, and createContext spreads its result into the TRPC context, so ctx.prisma is always defined.

packages/app-store/zoomvideo/lib/VideoApiAdapter.ts (1)

320-322: Good: JSON type cast is explicit and narrow.

Casting newTokenObject to Prisma.InputJsonValue is appropriate here, given trusted OAuth manager input.

packages/lib/server/service/InsightsRoutingBaseService.ts (1)

15-16: Type-only migration to PrismaClient looks good.

No runtime changes; aligns with the repo-wide typing refactor.

Also applies to: 139-149

packages/lib/server/repository/booking.ts (2)

12-21: FormResponse type addition is sensible.

Clearer typing for routing form responses.


668-702: Replace Prisma include with explicit select. Avoid loading entire related entities; confirm which relation fields callers actually use and update the query to select only those.

apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (1)

320-360: Routes/fields cloning approach looks good and the options now required aligns with type changes.

No issues spotted with the new RouteConfig/action/queryValue shape.

Also applies to: 388-498, 499-561

packages/prisma/index.ts (2)

2-2: LGTM: switch to named PrismaClient import

Consistent with the new pattern and improves clarity.


74-74: LGTM: dev-only global caching
Prevents multiple clients during HMR while keeping production clean.

Comment on lines +842 to 850
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
attendees: true,
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
references: true,
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
user: true,
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
payment: true,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use select instead of include for getValidBookingFromEventTypeForAttendee.

Trim related payloads to what the caller needs.

-      include: {
-        // eslint-disable-next-line @calcom/eslint/no-prisma-include-true
-        attendees: true,
-        // eslint-disable-next-line @calcom/eslint/no-prisma-include-true
-        references: true,
-        // eslint-disable-next-line @calcom/eslint/no-prisma-include-true
-        user: true,
-        // eslint-disable-next-line @calcom/eslint/no-prisma-include-true
-        payment: true,
-      },
+      select: {
+        attendees: { select: { id: true, email: true, name: true } },
+        references: { select: { id: true, uid: true, type: true, meetingUrl: true } },
+        user: { select: { id: true } },
+        payment: { select: { id: true } },
+      },
📝 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.

Suggested change
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
attendees: true,
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
references: true,
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
user: true,
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
payment: true,
},
select: {
attendees: { select: { id: true, email: true, name: true } },
references: { select: { id: true, uid: true, type: true, meetingUrl: true } },
user: { select: { id: true } },
payment: { select: { id: true } },
},
🤖 Prompt for AI Agents
In packages/lib/server/repository/booking.ts around lines 842 to 850, the query
is using include for attendees, references, user, and payment; change these to
select and only fetch the specific fields the caller needs (e.g., for attendees:
id, email, name; for references: id, type, value; for user: id, email, timezone;
for payment: id, status, amount — adjust to actual required properties). Update
the returned type annotations if necessary and remove the eslint-disable
comments, ensuring the query returns a smaller payload tailored to the caller.

Comment on lines 95 to 101
return await prisma.workspacePlatform.update({
where: { id },
data,
data: {
...data,
defaultServiceAccountKey: data.defaultServiceAccountKey as unknown as Prisma.InputJsonValue,
},
select: safeWorkspacePlatformSelect,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid writing undefined for defaultServiceAccountKey on partial updates.

Only set the field when provided; map explicit null to Prisma.JsonNull.

-    return await prisma.workspacePlatform.update({
-      where: { id },
-      data: {
-        ...data,
-        defaultServiceAccountKey: data.defaultServiceAccountKey as unknown as Prisma.InputJsonValue,
-      },
-      select: safeWorkspacePlatformSelect,
-    });
+    const updateData: any = { ...data };
+    if ("defaultServiceAccountKey" in data) {
+      updateData.defaultServiceAccountKey =
+        data.defaultServiceAccountKey === null
+          ? Prisma.JsonNull
+          : (data.defaultServiceAccountKey as unknown as Prisma.InputJsonValue);
+    }
+    return await prisma.workspacePlatform.update({
+      where: { id },
+      data: updateData,
+      select: safeWorkspacePlatformSelect,
+    });
📝 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.

Suggested change
return await prisma.workspacePlatform.update({
where: { id },
data,
data: {
...data,
defaultServiceAccountKey: data.defaultServiceAccountKey as unknown as Prisma.InputJsonValue,
},
select: safeWorkspacePlatformSelect,
const updateData: any = { ...data };
if ("defaultServiceAccountKey" in data) {
updateData.defaultServiceAccountKey =
data.defaultServiceAccountKey === null
? Prisma.JsonNull
: (data.defaultServiceAccountKey as unknown as Prisma.InputJsonValue);
}
return await prisma.workspacePlatform.update({
where: { id },
data: updateData,
select: safeWorkspacePlatformSelect,
});
🤖 Prompt for AI Agents
In packages/lib/server/repository/workspacePlatform.ts around lines 95 to 101,
the update currently spreads data and always writes defaultServiceAccountKey
(which can become undefined) — change the update payload to only include
defaultServiceAccountKey when it is present on the incoming data: build the data
object so that you spread the other fields, and conditionally add
defaultServiceAccountKey only if Object.prototype.hasOwnProperty.call(data,
'defaultServiceAccountKey'), mapping an explicit null to Prisma.JsonNull and
otherwise casting the value to Prisma.InputJsonValue; this prevents writing
undefined on partial updates and preserves intentional nulls.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 9, 2025

E2E results are ready!

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: 0

🧹 Nitpick comments (6)
apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (6)

32-37: Replace any with a minimal query-AST type; drop eslint disables.

Keeps inference off but preserves safety.

Apply within the interface:

-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  queryValue?: any;
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  attributesQueryValue?: any;
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  fallbackAttributesQueryValue?: any;
+  queryValue?: QueryGroup;
+  attributesQueryValue?: QueryGroup;
+  fallbackAttributesQueryValue?: QueryGroup;

Add near the interfaces (outside this range):

type QueryRule = {
  type: "rule";
  properties: {
    field: string;
    value: unknown[] | unknown[][];
    operator: string;
    valueSrc: string[];
    valueType: string[];
    valueError?: (string | null)[];
  };
};
type QueryGroup = { id: string; type: "group"; children1?: Record<string, QueryRule | QueryGroup> };

135-143: Run team event creations in parallel and remove the any map.

The map isn’t used; parallelizing reduces test time.

-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const teamEvents: { [key: string]: any } = {};
-for (const eventConfig of teamEventsConfig) {
-  const eventType = await createRoundRobinTeamEventType({
-    teamId,
-    eventType: { ...eventConfig, length: eventConfig.length || 60 },
-  });
-  teamEvents[eventConfig.slug] = eventType;
-}
+await Promise.all(
+  teamEventsConfig.map((eventConfig) =>
+    createRoundRobinTeamEventType({
+      teamId,
+      eventType: { ...eventConfig, length: eventConfig.length || 60 },
+    })
+  )
+);

156-158: Avoid non-null assertion on Skills attribute; fail fast with a clear error.

Prevents a cryptic runtime throw when config omits “Skills”.

-// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-const skillAttribute = attributes.find((attr) => attr.name === "Skills")!;
+const skillAttribute = attributes.find((attr) => attr.name === "Skills");
+if (!skillAttribute) {
+  throw new Error('Attribute "Skills" not found in attributeRouting.attributes');
+}

321-358: UI copy consistency: capitalize “Skills”.

Matches the “Location” label.

-              label: "skills",
+              label: "Skills",

387-497: Minor: rename map var to route and dedupe the repeated field id.

Improves readability and avoids string duplication.

Add once near Line 376 (outside this range):

const textFieldUuid = "c4296635-9f12-47b1-8153-c3a854649182";

Then:

-        routes: (
+        routes: (
           config.form?.routes ||
           ([
             {
               id: "8a898988-89ab-4cde-b012-31823f708642",
               action: { type: "eventTypeRedirectUrl", value: "pro/30min" },
               queryValue: {
                 id: "8a898988-89ab-4cde-b012-31823f708642",
                 type: "group",
                 children1: {
                   "8988bbb8-0123-4456-b89a-b1823f70c5ff": {
                     type: "rule",
                     properties: {
-                      field: "c4296635-9f12-47b1-8153-c3a854649182",
+                      field: textFieldUuid,
                       value: ["event-routing"],
                       operator: "equal",
                       valueSrc: ["value"],
                       valueType: ["text"],
                     },
                   },
                 },
               },
             },
             ...
-          ] satisfies RouteConfig[])
-        ).map((field) => ({ ...field })),
+          ] satisfies RouteConfig[])
+        ).map((route) => ({ ...route })),

498-558: Use the same constant for the Test field id here too.

Prevents drift between rules and field definitions.

-            {
-              id: "c4296635-9f12-47b1-8153-c3a854649182",
+            {
+              id: textFieldUuid,
               type: "text",
               label: "Test field",
               required: true,
             },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • 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 0a3ecc5 and 9446171.

📒 Files selected for processing (1)
  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (5 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:

  • apps/web/playwright/lib/test-helpers/routingFormHelpers.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:

  • apps/web/playwright/lib/test-helpers/routingFormHelpers.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:

  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: emrysal
PR: calcom/cal.com#23692
File: packages/lib/server/service/InsightsBookingBaseService.ts:16-16
Timestamp: 2025-09-09T03:29:42.984Z
Learning: In the Cal.com codebase, readonlyPrisma is still an instance of PrismaClient, making type changes from `typeof readonlyPrisma` to `PrismaClient` less critical since they are fundamentally compatible types.
📚 Learning: 2025-07-15T12:59:34.389Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Applied to files:

  • apps/web/playwright/lib/test-helpers/routingFormHelpers.ts
🧬 Code graph analysis (1)
apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (1)
packages/lib/constants.ts (1)
  • WEBAPP_URL (12-18)
🔇 Additional comments (1)
apps/web/playwright/lib/test-helpers/routingFormHelpers.ts (1)

216-371: Drop the select suggestion for this test helper
createRoutingForm is used by tests that access form.name, form.userId, form.routes, and form.fields; restricting the returned fields to only id would break existing tests.

Likely an incorrect or invalid review comment.

prismaMock.booking.findUnique.mockResolvedValue({
...originalBooking,
status: "cancelled",
status: "CANCELLED",
Copy link
Contributor

@volnei volnei Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: would it impact on all queries?
should we use constant BookingStatus.CANCELLED?

@@ -5,6 +5,7 @@ import { z } from "zod";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import prisma from "@calcom/prisma";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you change it for use the named export?

import { prisma } from "@calcom/prisma";

volnei
volnei previously approved these changes Sep 9, 2025
Copy link
Contributor

@volnei volnei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit comments... but looks good to me!

@emrysal emrysal merged commit aa51218 into main Sep 9, 2025
37 of 38 checks passed
@emrysal emrysal deleted the perf/prisma-disable-inference-client-extensions branch September 9, 2025 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core area: core, team members only foundation 🧹 Improvements Improvements to existing features. Mostly UX/UI ready-for-e2e size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants