Skip to content

Conversation

@volnei
Copy link
Contributor

@volnei volnei commented Jan 2, 2026

What does this PR do?

Upgrades the TypeScript target to ES2022 across the monorepo to reduce transpilation and improve performance.


Summary by cubic

Upgraded the TypeScript target to ES2022 across the monorepo to reduce transpilation and improve performance. Moved field initializations into constructors and switched to type-only class properties to align with ES2022 semantics.

  • Refactors

    • Set compilerOptions.target to ES2022 in base and package tsconfigs (API v2, app-store-cli, embeds, platform, lib, trpc, Next.js/react-library).
    • Moved field initializations into constructors (AvailableSlotsService, calendar/conferencing services, Stripe, Gcal controller, Teams service).
    • Replaced definite assignment assertions with declare in platform outputs and selected API v2 DTOs to avoid runtime class fields under ES2022.
    • Decorators: use type-only import for SortOrderType and enable unsafeParameterDecoratorsEnabled in Biome.
    • Migrated platform packages to ESM (type=module) and updated platform-libraries i18n to ESM imports.
  • Bug Fixes

    • Updated RequiresAtLeastOnePropertyWhenNotDisabled validator to count only defined values, since ES2022 emits undefined class fields at runtime.

Written for commit d01dac4. Summary will update on new commits.

ES2022 Compatibility Fixes

The ES2022 target changes class field semantics, requiring the following fixes:

TS2612 Errors (Property will overwrite base property):

  • booking-fields.output.ts - Added declare modifier to properties in 19 output classes
  • booking.output.ts - Fixed bookingFieldsResponses in RecurringBookingOutput_2024_08_13
  • event-type.output.ts - Fixed hideCalendarEventDetails in TeamEventTypeOutput_2024_06_14
  • team.output.ts - Fixed parentId in OrgTeamOutputDto

TS1272 Errors (Type imports with decorators):

  • get-event-types-query.input.ts - Changed to import type for SortOrderType

CI Status

  • Type check: ✓ Passing
  • Linters: ✓ Passing
  • Unit tests: ✓ Passing
  • API v2 Unit tests: Pre-existing Jest configuration issues (unrelated to this PR)

Link to Devin run: https://app.devin.ai/sessions/9bf85eb1d10f4045b6ca88c0ef2c2085
Requested by: Volnei Munhoz (@volnei)

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. N/A - no documentation changes needed.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Run yarn type-check:ci --force to verify no new type errors are introduced
  2. Run yarn lint to verify linting passes
  3. Run yarn test to verify unit tests pass
  4. Build the API v2 app to verify the ES2022 target works correctly

Human Review Checklist

  • Verify the declare modifier changes don't affect runtime behavior of NestJS DTOs with decorators
  • Verify the AvailableSlotsService constructor refactor maintains the same functionality
  • Confirm API v2 Unit test failures are pre-existing and unrelated to these changes

@vercel
Copy link

vercel bot commented Jan 2, 2026

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

3 Skipped Deployments
Project Deployment Review Updated (UTC)
cal Ignored Ignored Jan 2, 2026 5:24pm
cal-companion Ignored Ignored Preview Jan 2, 2026 5:24pm
cal-eu Ignored Ignored Jan 2, 2026 5:24pm

…tibility

With ES2022 target, class properties with definite assignment assertion (!)
are treated as runtime class fields that overwrite base class properties,
causing TS2612 errors. Adding the 'declare' modifier makes these properties
type-only, preventing runtime field emission while preserving type narrowing.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…rget

Fixed additional TS2612 errors in:
- event-type.output.ts: hideCalendarEventDetails property
- team.output.ts: parentId property

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
With ES2022 target + isolatedModules + emitDecoratorMetadata, TypeScript
requires type-only imports for types used in decorated signatures.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
@devin-ai-integration devin-ai-integration bot changed the title chore: upgrate ts target (perf) chore: upgrade ts target (perf) Jan 2, 2026
@volnei volnei added ready-for-e2e run-ci Approve CI to run for external contributors labels Jan 2, 2026
volnei and others added 2 commits January 2, 2026 11:15
…ompatibility

When target is ES2022, TypeScript defaults module to ES6 which outputs ESM
syntax. This breaks Jest tests that expect CommonJS. Explicitly setting
module: CommonJS maintains the previous behavior while still using ES2022
target for syntax features.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
@github-actions
Copy link
Contributor

github-actions bot commented Jan 2, 2026

E2E results are ready!

When target is ES2022, TypeScript defaults module to ES6 which outputs ESM
syntax. This breaks Jest tests that expect CommonJS. Explicitly setting
module: CommonJS in enums, utils, and types packages maintains the previous
behavior while still using ES2022 target for syntax features.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…S2022 compatibility

In ES2022, class fields are initialized in declaration order before the
constructor runs. Properties that depend on constructor parameters must
be initialized in the constructor, not as field initializers.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…S2022 compatibility

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…S2022 compatibility in API v2

- Fix TS2729 errors by moving field initializations to constructor body
- Fix TS2612 error by adding declare modifier to workflow-step.input.ts
- Fix TS2322 error by changing return type in teams.controller.ts
- Enable unsafeParameterDecoratorsEnabled in biome.json for NestJS decorators

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…22 compatibility

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…S2022 class fields

With ES2022 target, class fields are emitted as runtime properties even when undefined.
This caused Object.keys() to include properties like maximumActiveBookings and offerReschedule
even when the user only sent { disabled: false }, making the validation incorrectly pass.

The fix checks for properties with defined values instead of just checking for key existence.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
The ES2022 target works without explicit module: CommonJS setting.
Also removed unnecessary comment from validator fix.

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
volnei and others added 2 commits January 2, 2026 14:21
* feat: add type=module to platform packages for ESM migration

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>

* refactor: remove CommonJS from platform-libraries and convert to ESM imports

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@diffray-bot
Copy link

Changes Summary

This PR upgrades the TypeScript compilation target to ES2022 across the monorepo to reduce transpilation overhead and improve performance. The changes include updating tsconfig files, adjusting class field initialization patterns to align with ES2022 semantics, and migrating platform packages to ESM (type=module).

Type: refactoring

Components Affected: API v2 services (Calendar, Conferencing, Teams, Stripe, Workflows), TypeScript configuration (base, Next.js, React Library), Platform packages (constants, enums, types, libraries, utils), Embeds (core, react, snippet), TRPC server, Biome linter configuration, Validation utilities

Files Changed
File Summary Change Impact
packages/tsconfig/base.json Updated TypeScript target from ES2020 to ES2022 ✏️ 🔴
packages/tsconfig/nextjs.json Updated TypeScript target to ES2022 ✏️ 🔴
packages/tsconfig/react-library.json Updated TypeScript target to ES2022 ✏️ 🔴
apps/api/v2/tsconfig.json Updated TypeScript target to ES2022 ✏️ 🔴
apps/api/v2/src/modules/stripe/stripe.service.ts Moved config-dependent field initializations from property declarations to constructor with null coalescing fallbacks ✏️ 🟡
...pi/v2/src/ee/calendars/services/gcal.service.ts Moved field initializations to constructor for ES2022 compatibility ✏️ 🟡
...v2/src/ee/calendars/services/outlook.service.ts Moved field initializations to constructor for ES2022 compatibility ✏️ 🟡
...onferencing/services/office365-video.service.ts Moved field initializations to constructor for ES2022 compatibility ✏️ 🟡
...les/conferencing/services/zoom-video.service.ts Moved field initializations to constructor for ES2022 compatibility ✏️ 🟡
...s/bookings/2024-08-13/outputs/booking.output.ts Replaced definite assignment assertion (!) with declare modifier for bookingFieldsResponses ✏️ 🟡
...pes_2024_06_14/outputs/booking-fields.output.ts Added declare modifier to 19+ class properties to prevent runtime class field emission in ES2022 ✏️ 🟡
...024_06_14/inputs/get-event-types-query.input.ts Changed SortOrderType to type-only import to fix TS1272 decorator errors ✏️ 🟢
...ypes/organizations/teams/outputs/team.output.ts Added declare modifier to parentId property ✏️ 🟢
...utils/RequiresOneOfPropertiesWhenNotDisabled.ts Updated validator to count only defined values since ES2022 emits undefined class fields at runtime ✏️ 🟡
packages/platform/libraries/i18n.ts Migrated from CommonJS require to ESM imports with fileURLToPath and createRequire utilities ✏️ 🟡
packages/platform/constants/package.json Added type=module for ESM configuration ✏️ 🟡
packages/platform/enums/package.json Added type=module for ESM configuration ✏️ 🟡
packages/platform/types/package.json Added type=module for ESM configuration ✏️ 🟡
packages/platform/utils/package.json Added type=module for ESM configuration ✏️ 🟡
packages/trpc/server/routers/viewer/slots/util.ts Moved getAvailableSlots method initialization to constructor for ES2022 compatibility ✏️ 🟡
biome.json Added unsafeParameterDecoratorsEnabled to support decorators with ES2022 target ✏️ 🟢
Architecture Impact
  • New Patterns: Deferred field initialization pattern (declaration in property, assignment in constructor), Type-only imports for decorator usage, ESM module format for platform packages
  • Dependencies: TypeScript target compilation (ES2020 -> ES2022), Module format changes (CommonJS -> ESM for platform packages)
  • Coupling: No significant coupling changes; primarily aligns existing code with new compilation target semantics
  • Breaking Changes: ES2022 target may affect runtime behavior of class fields if not properly migrated (mitigated by declare modifier usage), Platform packages now export as ESM (type=module) which may affect import resolution for CommonJS consumers

Risk Areas: Runtime behavior changes: ES2022 class field semantics differ from earlier targets - class fields are now initialized before constructor, requiring deferred initialization for config-dependent values, NestJS DTO decorators: Using declare modifier on decorated properties requires verification that decorators still function correctly at runtime, ESM migration: Platform packages switching to type=module may cause import resolution issues in consuming code, Validation logic: RequiresAtLeastOnePropertyWhenNotDisabled validator fix depends on correct handling of undefined fields in ES2022, Module resolution: i18n.ts using createRequire in ESM context requires careful testing of i18n functionality

Suggestions
  • Verify that NestJS class-validator decorators work correctly with declare modifier on properties in all affected DTOs
  • Test calendar integration services (GCal, Outlook) to ensure config-dependent initialization works correctly
  • Run full integration tests on API v2 services (Stripe, Teams, Conferencing) to catch any runtime initialization issues
  • Verify ESM compatibility with all platform package consumers, particularly checking module resolution and import paths
  • Test the RequiresAtLeastOnePropertyWhenNotDisabled validator with various input scenarios to ensure undefined value handling is correct
  • Verify available slots computation (AvailableSlotsService) maintains the same performance characteristics after constructor refactoring

Full review in progress... | Powered by diffray

Comment on lines 19 to 20
const translationCache = new Map<string, Record<string, string>>([["en-common", englishTranslations]]);
const i18nInstanceCache = new Map<string, I18nInstance>();

Choose a reason for hiding this comment

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

🟠 HIGH - Module-level mutable caches without cleanup strategy
Agent: nodejs

Category: bug

Description:
Two module-level Maps (translationCache, i18nInstanceCache) accumulate data indefinitely. translationCache.set() called at line 55, i18nInstanceCache.set() at line 90. No size limits, no TTL, no cleanup mechanism.

Suggestion:
Implement cache management with LRU eviction, TTL-based expiration, or document maximum size limits. Use lru-cache package or add periodic cleanup mechanism.

Confidence: 85%
Rule: node_module_level_state
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

// Button added here triggers the modal on click. So, it has to have the same data attributes as the modal target as well
dataset!: DOMStringMap & FloatingButtonDataset & ModalTargetDatasetProps;
declare dataset: DOMStringMap & FloatingButtonDataset & ModalTargetDatasetProps;
buttonWrapperStyleDisplay!: HTMLElement["style"]["display"];

Choose a reason for hiding this comment

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

🟠 HIGH - Uninitialized property access on toggle callback
Agent: bugs

Category: bug

Description:
Property buttonWrapperStyleDisplay declared with ! assertion at line 46 but never initialized. When data-toggle-off callback triggers with off=false before first toggle-off=true event, this.buttonWrapperStyleDisplay is undefined at line 84.

Suggestion:
Initialize buttonWrapperStyleDisplay in constructor or provide fallback: buttonWrapperEl.style.display = off ? 'none' : (this.buttonWrapperStyleDisplay ?? 'block');

Confidence: 85%
Rule: ts_validate_nullable_before_use
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines 5 to 6
"main": "./dist/index.ts",
"types": "./dist/index.ts",

Choose a reason for hiding this comment

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

🟠 HIGH - Incorrect main and types entry points with TS source files
Agent: Delegated (nodejs, performance, quality, react)

Category: bug

Description:
package.json specifies main and types pointing to './dist/index.ts' (TypeScript source file) instead of compiled output. However, exports field at lines 8-13 correctly points to .js and .d.ts.map files, which may override these.

Suggestion:
Update package.json entries: main: './dist/index.js', types: './dist/index.d.ts' for consistency with exports field.

Confidence: 75%
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

"version": "0.0.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.js",

Choose a reason for hiding this comment

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

🟠 HIGH - Incorrect types entry point pointing to compiled JS instead of .d.ts
Agent: Delegated (nodejs, performance, quality, react)

Category: bug

Description:
The types entry points to './dist/index.js' (compiled JavaScript) instead of './dist/index.d.ts' (TypeScript declarations). TypeScript uses the 'types' field to resolve type information.

Suggestion:
Update package.json: types: './dist/index.d.ts' to correctly point to TypeScript declaration files.

Confidence: 90%
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines 1 to 7
{
"extends": "@calcom/tsconfig/base.json",
"compilerOptions": {
"target": "ES5",
"target": "ES2022",
"resolveJsonModule": true,
"experimentalDecorators": true,
"baseUrl": "./",

Choose a reason for hiding this comment

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

🟠 HIGH - Missing TypeScript project reference configuration
Agent: react

Category: quality

Description:
tsconfig.json is missing composite, declaration, and declarationMap settings. Package depends on @calcom/platform-constants (line 26 in package.json) but lacks proper project reference configuration for monorepo builds.

Suggestion:
Add to compilerOptions: "composite": true, "declaration": true, "declarationMap": true, and add 'references' array.

Confidence: 70%
Rule: ts_enable_ts_project_references_for_depende
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

/* eslint-enable @typescript-eslint/no-require-imports */

const translationCache = new Map<string, Record<string, string>>([["en-common", englishTranslations]]);
const i18nInstanceCache = new Map<string, I18nInstance>();

Choose a reason for hiding this comment

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

🟡 MEDIUM - Hardcoded locale transformation without configuration
Agent: quality

Category: quality

Description:
Locale transformation for 'zh' to 'zh-CN' is hardcoded. Should be configurable for maintainability.

Suggestion:
Define const LOCALE_TRANSFORMATIONS = { 'zh': 'zh-CN' } and use it.

Why this matters: Simpler code is easier to understand, test, and maintain. Reducing unnecessary abstraction lowers cognitive load and reduces places where bugs can hide. Standard library functions are well-tested and familiar to most developers.

Confidence: 60%
Rule: quality_prefer_simple_readable_code
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

client_secret: z.string(),
});

@Injectable()

Choose a reason for hiding this comment

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

🟠 HIGH - CommonJS require() import for qs-stringify
Agent: react

Category: quality

Description:
Using TypeScript's import = require() syntax which compiles to CommonJS. While valid, this pattern may cause issues in pure ESM contexts.

Suggestion:
Convert to ES Module import: 'import stringify from "qs-stringify";' if the package supports ESM.

Confidence: 75%
Rule: ts_use_es_modules_import_export
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +5 to 17
import { createRequire } from "node:module";

import { WEBAPP_URL } from "@calcom/lib/constants";
import { fetchWithTimeout } from "@calcom/lib/fetchWithTimeout";
import logger from "@calcom/lib/logger";

/* eslint-disable @typescript-eslint/no-require-imports */
const { i18n } = require("@calcom/config/next-i18next.config");
const path = require("node:path");
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const require = createRequire(import.meta.url);
const i18nConfig = require("@calcom/config/next-i18next.config");
const { i18n } = i18nConfig;
const translationsPath = path.resolve(__dirname, "../../../../apps/web/public/static/locales/en/common.json");
const englishTranslations: Record<string, string> = require(translationsPath);

Choose a reason for hiding this comment

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

🟠 HIGH - Using createRequire for CommonJS require() in ESM context
Agent: react

Category: quality

Description:
File uses createRequire to load CommonJS modules (next-i18next.config and translation JSON). While valid in Node.js, this may cause issues if full ESM migration is desired.

Suggestion:
Consider using dynamic imports or ensuring config files support ESM. However, this pattern is sometimes necessary for JSON imports in ESM.

Confidence: 65%
Rule: ts_use_es_modules_import_export
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

/* eslint-enable @typescript-eslint/no-require-imports */

const translationCache = new Map<string, Record<string, string>>([["en-common", englishTranslations]]);
const i18nInstanceCache = new Map<string, I18nInstance>();

Choose a reason for hiding this comment

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

🟡 MEDIUM - Potential null dereference without validation
Agent: typescript

Category: quality

Description:
Map.get() returns T | undefined even after has() check. TypeScript doesn't narrow the type after has() because of potential race conditions.

Suggestion:
Use: const cached = translationCache.get(cacheKey); if (cached) { return cached; }

Confidence: 70%
Rule: ts_strict_null_checks
Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

@diffray-bot
Copy link

Review Summary

Free public review - Want AI code reviews on your PRs? Check out diffray.ai

Validated 46 issues: 26 kept, 20 filtered

Issues Found: 26

💬 See 9 individual line comment(s) for details.

📊 14 unique issue type(s) across 26 location(s)

📋 Full issue list (click to expand)

🔴 CRITICAL - Null pointer dereference in validateDefaultField (2 occurrences)

Agent: bugs

Category: bug

📍 View all locations
File Description Suggestion Confidence
packages/platform/types/event-types/event-types_2024_06_14/outputs/booking-fields.output.ts:727-735 ClassType retrieved from defaultOutputNameMap could be undefined if slug doesn't exist in map. Code ... Add early return after the undefined check to prevent plainToInstance from being called with undefin... 95%
packages/platform/types/event-types/event-types_2024_06_14/outputs/booking-fields.output.ts:745-753 ClassType retrieved from customOutputTypeMap could be undefined if type doesn't exist in map. Code l... Add early return after the undefined check to prevent plainToInstance from being called with undefin... 95%

Rule: bug_null_pointer


🔴 CRITICAL - Unsafe property access on JSON parsed data (2 occurrences)

Agent: bugs

Category: bug

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/stripe/stripe.service.ts:160-163 Code at line 161 parses credentials.key and immediately accesses stripe_user_id at line 163 with opt... Add validation after JSON.parse to ensure parsed object has expected structure before accessing stri... 80%
apps/api/v2/src/ee/calendars/services/outlook.service.ts:189-191 getOAuthCredentials (lines 120-149) returns raw JSON response without validation - just 'return resp... Add validation in getOAuthCredentials to check response.ok and validate response shape before return... 85%

Rule: ts_unsafe_property_access_external_data


🟠 HIGH - Module-level mutable caches without cleanup strategy

Agent: nodejs

Category: bug

File: packages/platform/libraries/i18n.ts:19-20

Description: Two module-level Maps (translationCache, i18nInstanceCache) accumulate data indefinitely. translationCache.set() called at line 55, i18nInstanceCache.set() at line 90. No size limits, no TTL, no cleanup mechanism.

Suggestion: Implement cache management with LRU eviction, TTL-based expiration, or document maximum size limits. Use lru-cache package or add periodic cleanup mechanism.

Confidence: 85%

Rule: node_module_level_state


🟠 HIGH - Uninitialized property access on toggle callback

Agent: bugs

Category: bug

File: packages/embeds/embed-core/src/FloatingButton/FloatingButton.ts:46

Description: Property buttonWrapperStyleDisplay declared with ! assertion at line 46 but never initialized. When data-toggle-off callback triggers with off=false before first toggle-off=true event, this.buttonWrapperStyleDisplay is undefined at line 84.

Suggestion: Initialize buttonWrapperStyleDisplay in constructor or provide fallback: buttonWrapperEl.style.display = off ? 'none' : (this.buttonWrapperStyleDisplay ?? 'block');

Confidence: 85%

Rule: ts_validate_nullable_before_use


🟠 HIGH - Incorrect main and types entry points with TS source files

Agent: Delegated (nodejs, performance, quality, react)

Category: bug

File: packages/platform/enums/package.json:5-6

Description: package.json specifies main and types pointing to './dist/index.ts' (TypeScript source file) instead of compiled output. However, exports field at lines 8-13 correctly points to .js and .d.ts.map files, which may override these.

Suggestion: Update package.json entries: main: './dist/index.js', types: './dist/index.d.ts' for consistency with exports field.

Confidence: 75%


🟠 HIGH - Incorrect types entry point pointing to compiled JS instead of .d.ts

Agent: Delegated (nodejs, performance, quality, react)

Category: bug

File: packages/platform/types/package.json:6

Description: The types entry points to './dist/index.js' (compiled JavaScript) instead of './dist/index.d.ts' (TypeScript declarations). TypeScript uses the 'types' field to resolve type information.

Suggestion: Update package.json: types: './dist/index.d.ts' to correctly point to TypeScript declaration files.

Confidence: 90%


🟠 HIGH - Missing TypeScript project reference configuration

Agent: react

Category: quality

File: packages/platform/enums/tsconfig.json:1-7

Description: tsconfig.json is missing composite, declaration, and declarationMap settings. Package depends on @calcom/platform-constants (line 26 in package.json) but lacks proper project reference configuration for monorepo builds.

Suggestion: Add to compilerOptions: "composite": true, "declaration": true, "declarationMap": true, and add 'references' array.

Confidence: 70%

Rule: ts_enable_ts_project_references_for_depende


🟠 HIGH - CommonJS require() import for qs-stringify (2 occurrences)

Agent: react

Category: quality

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/conferencing/services/zoom-video.service.ts:19 Using TypeScript's import = require() syntax which compiles to CommonJS. While valid, this pattern... Convert to ES Module import: 'import stringify from "qs-stringify";' if the package supports ESM. 75%
packages/platform/libraries/i18n.ts:5-17 File uses createRequire to load CommonJS modules (next-i18next.config and translation JSON). While v... Consider using dynamic imports or ensuring config files support ESM. However, this pattern is someti... 65%

Rule: ts_use_es_modules_import_export


🟡 MEDIUM - Magic numbers for environment-dependent timeout (2 occurrences)

Agent: quality

Category: quality

Why this matters: Simpler code is easier to understand, test, and maintain. Reducing unnecessary abstraction lowers cognitive load and reduces places where bugs can hide. Standard library functions are well-tested and familiar to most developers.

📍 View all locations
File Description Suggestion Confidence
packages/platform/libraries/i18n.ts:46 Timeout values (30000 vs 3000) are hardcoded without named constants. Makes maintenance harder and u... Define const DEVELOPMENT_TIMEOUT = 30000; const PRODUCTION_TIMEOUT = 3000; 62%
packages/platform/libraries/i18n.ts:20 Locale transformation for 'zh' to 'zh-CN' is hardcoded. Should be configurable for maintainability. Define const LOCALE_TRANSFORMATIONS = { 'zh': 'zh-CN' } and use it. 60%

Rule: quality_prefer_simple_readable_code


🟡 MEDIUM - Loose equality comparison with string literal (2 occurrences)

Agent: refactoring

Category: quality

📍 View all locations
File Description Suggestion Confidence
packages/embeds/embed-core/src/FloatingButton/FloatingButton.ts:71 Using loose equality (==) to compare newValue with string 'true'. Should use strict equality (===) f... Replace 'newValue == "true"' with 'newValue === "true"' 75%
packages/embeds/embed-core/src/FloatingButton/FloatingButton.ts:79 Using loose equality (==) to compare newValue with string 'true'. Should use strict equality (===) f... Replace 'newValue == "true"' with 'newValue === "true"' 75%

Rule: bug_loose_equality


🟡 MEDIUM - Unsafe any type in z.record pattern (2 occurrences)

Agent: typescript

Category: quality

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/stripe/stripe.service.ts:81 Using z.any() in z.record loses type safety for stripe connect parameters. This makes it difficult t... Replace z.any() with a more specific schema that matches Stripe.OAuthAuthorizeUrlParams structure 70%
packages/trpc/server/routers/viewer/slots/util.ts:86-88 The troubleshooter field is typed as 'any', bypassing type safety. This makes the structure undocume... Define a TroubleshooterData interface with proper types for the troubleshooter object used at lines ... 65%

Rule: ts_prefer_unknown_over_any


🟡 MEDIUM - Unnecessary JSON stringify/parse roundtrip

Agent: typescript

Category: quality

File: apps/api/v2/src/modules/stripe/stripe.service.ts:160-161

Description: Converting credentials.key to JSON string and back is unnecessary overhead and can lose type information.

Suggestion: Access credentials.key directly with proper typing: const stripeKeyObject = credentials.key as Record<string, unknown>

Confidence: 75%

Rule: ts_type_safety_over_dry


🟡 MEDIUM - Unsafe type casting chain for metadata access (6 occurrences)

Agent: typescript

Category: quality

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/stripe/stripe.service.ts:234 Using double type assertion (as Prisma.JsonObject then as string) without validation. If metadata st... Validate metadata structure before accessing or use a typed helper function with proper null checks 72%
apps/api/v2/src/modules/stripe/stripe.service.ts:257 Casting user.metadata as Prisma.JsonObject without validation. If metadata is null or malformed, the... Add defensive check: ...(user.metadata && typeof user.metadata === 'object' ? user.metadata : {}) 68%
apps/api/v2/src/ee/calendars/services/gcal.service.ts:190-205 OAuth token.tokens object is cast directly to Prisma.InputJsonValue without validation. Google OAuth... Validate token structure with Zod schema before casting, similar to how gcalResponseSchema is used f... 65%
apps/api/v2/src/ee/calendars/services/outlook.service.ts:189-227 office365OAuthCredentials from getOAuthCredentials() is passed directly to createAndLinkCalendarEntr... Add Zod validation for the OAuth response in getOAuthCredentials() before returning. 65%
apps/api/v2/src/modules/conferencing/services/office365-video.service.ts:122-128 responseBody is cast via 'as unknown as Prisma.InputJsonObject' at lines 122 and 128, bypassing type... Create and validate responseBody against a Zod schema before casting to InputJsonObject. 65%
apps/api/v2/src/modules/conferencing/services/zoom-video.service.ts:115-121 responseBody is cast via 'as unknown as Prisma.InputJsonObject' at lines 115 and 121, bypassing type... Create and validate responseBody against a Zod schema before casting to InputJsonObject. 65%

Rule: ts_non_null_assertion


🟡 MEDIUM - Potential null dereference without validation (2 occurrences)

Agent: typescript

Category: quality

📍 View all locations
File Description Suggestion Confidence
packages/platform/libraries/i18n.ts:20 Map.get() returns T | undefined even after has() check. TypeScript doesn't narrow the type after ha... Use: const cached = translationCache.get(cacheKey); if (cached) { return cached; } 70%
apps/api/v2/src/modules/conferencing/services/office365-video.service.ts:101-104 graphUser.mail ?? graphUser.userPrincipalName assigns undefined to responseBody.email if both proper... Add validation: if (!graphUser.mail && !graphUser.userPrincipalName) throw new BadRequestException('... 75%

Rule: ts_strict_null_checks


ℹ️ 17 issue(s) outside PR diff (click to expand)

These issues were found in lines not modified in this PR.

🔴 CRITICAL - Null pointer dereference in validateDefaultField (2 occurrences)

Agent: bugs

Category: bug

📍 View all locations
File Description Suggestion Confidence
packages/platform/types/event-types/event-types_2024_06_14/outputs/booking-fields.output.ts:727-735 ClassType retrieved from defaultOutputNameMap could be undefined if slug doesn't exist in map. Code ... Add early return after the undefined check to prevent plainToInstance from being called with undefin... 95%
packages/platform/types/event-types/event-types_2024_06_14/outputs/booking-fields.output.ts:745-753 ClassType retrieved from customOutputTypeMap could be undefined if type doesn't exist in map. Code l... Add early return after the undefined check to prevent plainToInstance from being called with undefin... 95%

Rule: bug_null_pointer


🔴 CRITICAL - Unsafe property access on JSON parsed data (2 occurrences)

Agent: bugs

Category: bug

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/stripe/stripe.service.ts:160-163 Code at line 161 parses credentials.key and immediately accesses stripe_user_id at line 163 with opt... Add validation after JSON.parse to ensure parsed object has expected structure before accessing stri... 80%
apps/api/v2/src/ee/calendars/services/outlook.service.ts:189-191 getOAuthCredentials (lines 120-149) returns raw JSON response without validation - just 'return resp... Add validation in getOAuthCredentials to check response.ok and validate response shape before return... 85%

Rule: ts_unsafe_property_access_external_data


🟡 MEDIUM - Magic numbers for environment-dependent timeout

Agent: quality

Category: quality

Why this matters: Simpler code is easier to understand, test, and maintain. Reducing unnecessary abstraction lowers cognitive load and reduces places where bugs can hide. Standard library functions are well-tested and familiar to most developers.

File: packages/platform/libraries/i18n.ts:46

Description: Timeout values (30000 vs 3000) are hardcoded without named constants. Makes maintenance harder and unclear why different timeouts are used.

Suggestion: Define const DEVELOPMENT_TIMEOUT = 30000; const PRODUCTION_TIMEOUT = 3000;

Confidence: 62%

Rule: quality_prefer_simple_readable_code


🟡 MEDIUM - Loose equality comparison with string literal (2 occurrences)

Agent: refactoring

Category: quality

📍 View all locations
File Description Suggestion Confidence
packages/embeds/embed-core/src/FloatingButton/FloatingButton.ts:71 Using loose equality (==) to compare newValue with string 'true'. Should use strict equality (===) f... Replace 'newValue == "true"' with 'newValue === "true"' 75%
packages/embeds/embed-core/src/FloatingButton/FloatingButton.ts:79 Using loose equality (==) to compare newValue with string 'true'. Should use strict equality (===) f... Replace 'newValue == "true"' with 'newValue === "true"' 75%

Rule: bug_loose_equality


🟡 MEDIUM - Unsafe any type in z.record pattern (2 occurrences)

Agent: typescript

Category: quality

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/stripe/stripe.service.ts:81 Using z.any() in z.record loses type safety for stripe connect parameters. This makes it difficult t... Replace z.any() with a more specific schema that matches Stripe.OAuthAuthorizeUrlParams structure 70%
packages/trpc/server/routers/viewer/slots/util.ts:86-88 The troubleshooter field is typed as 'any', bypassing type safety. This makes the structure undocume... Define a TroubleshooterData interface with proper types for the troubleshooter object used at lines ... 65%

Rule: ts_prefer_unknown_over_any


🟡 MEDIUM - Unnecessary JSON stringify/parse roundtrip

Agent: typescript

Category: quality

File: apps/api/v2/src/modules/stripe/stripe.service.ts:160-161

Description: Converting credentials.key to JSON string and back is unnecessary overhead and can lose type information.

Suggestion: Access credentials.key directly with proper typing: const stripeKeyObject = credentials.key as Record<string, unknown>

Confidence: 75%

Rule: ts_type_safety_over_dry


🟡 MEDIUM - Unsafe type casting chain for metadata access (6 occurrences)

Agent: typescript

Category: quality

📍 View all locations
File Description Suggestion Confidence
apps/api/v2/src/modules/stripe/stripe.service.ts:234 Using double type assertion (as Prisma.JsonObject then as string) without validation. If metadata st... Validate metadata structure before accessing or use a typed helper function with proper null checks 72%
apps/api/v2/src/modules/stripe/stripe.service.ts:257 Casting user.metadata as Prisma.JsonObject without validation. If metadata is null or malformed, the... Add defensive check: ...(user.metadata && typeof user.metadata === 'object' ? user.metadata : {}) 68%
apps/api/v2/src/ee/calendars/services/gcal.service.ts:190-205 OAuth token.tokens object is cast directly to Prisma.InputJsonValue without validation. Google OAuth... Validate token structure with Zod schema before casting, similar to how gcalResponseSchema is used f... 65%
apps/api/v2/src/ee/calendars/services/outlook.service.ts:189-227 office365OAuthCredentials from getOAuthCredentials() is passed directly to createAndLinkCalendarEntr... Add Zod validation for the OAuth response in getOAuthCredentials() before returning. 65%
apps/api/v2/src/modules/conferencing/services/office365-video.service.ts:122-128 responseBody is cast via 'as unknown as Prisma.InputJsonObject' at lines 122 and 128, bypassing type... Create and validate responseBody against a Zod schema before casting to InputJsonObject. 65%
apps/api/v2/src/modules/conferencing/services/zoom-video.service.ts:115-121 responseBody is cast via 'as unknown as Prisma.InputJsonObject' at lines 115 and 121, bypassing type... Create and validate responseBody against a Zod schema before casting to InputJsonObject. 65%

Rule: ts_non_null_assertion


🟡 MEDIUM - Unsafe optional chaining - email may be undefined

Agent: typescript

Category: quality

File: apps/api/v2/src/modules/conferencing/services/office365-video.service.ts:101-104

Description: graphUser.mail ?? graphUser.userPrincipalName assigns undefined to responseBody.email if both properties are undefined. This could cause downstream issues when email is expected.

Suggestion: Add validation: if (!graphUser.mail && !graphUser.userPrincipalName) throw new BadRequestException('User email not found')

Confidence: 75%

Rule: ts_strict_null_checks



Review ID: 0d8706e4-cb4f-47ad-8896-885907bfafa3
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

@volnei volnei closed this Jan 2, 2026
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 ready-for-e2e run-ci Approve CI to run for external contributors size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants