Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/test/lib/getSchedule/expects.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { diff } from "jest-diff";
import { expect } from "vitest";

import type { Slot } from "@calcom/trpc/server/routers/viewer/slots/types";
import type { Slot } from "@calcom/atoms/booker/types";

export const expectedSlotsForSchedule = {
IstWorkHours: {
Expand Down
12 changes: 6 additions & 6 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,17 +337,17 @@
"patterns": [
{
"group": [
"@calcom/trpc/react",
"@calcom/trpc/react/**"
"@calcom/trpc",
"@calcom/trpc/**"
],
"message": "atoms package should not import from @calcom/trpc/react."
"message": "atoms package should not import from @calcom/trpc."
},
{
"group": [
"../../trpc/react",
"../../trpc/react/**"
"../../trpc",
"../../trpc/**"
],
"message": "atoms package should not import from trpc/react."
"message": "atoms package should not import from trpc."
}
]
}
Expand Down
32 changes: 31 additions & 1 deletion packages/platform/atoms/booker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ import type {
ApiSuccessResponseWithoutData,
RoutingFormSearchParams,
} from "@calcom/platform-types";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots/types";

import type { UseCreateBookingInput } from "../hooks/bookings/useCreateBooking";

export type Slot = {
time: string;
userIds?: string[];

Choose a reason for hiding this comment

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

🚨 Bug: Slot.userIds: string[] contradicts upstream number[] type

The Slot.userIds field was changed from number[] to string[], but the upstream code that produces this data (in packages/features/schedules/lib/slots.ts) consistently types userIds as number[]. The TimeFrame type is { userIds?: number[]; startTime: number; endTime: number }, and all slot generation code works with numeric user IDs.

The original type in packages/trpc/server/routers/viewer/slots/types.ts (being deleted in this PR) had userIds?: number[]. Changing this to string[] in the atoms package means the type no longer matches the actual runtime data, which will cause type errors for any code that tries to use both the atom types and the internal slot types together (e.g., apps/web/test/lib/getSchedule/expects.ts which now imports from @calcom/atoms/booker/types).

Was this helpful? React with 👍 / 👎

Suggested change
userIds?: string[];
userIds?: number[];
  • Apply suggested fix

attendees?: number;
bookingUid?: string;
users?: string[];
};

// Type that includes only the data values from BookerStore (excluding functions)
export type BookerStoreValues = Omit<
BookerStore,
Expand Down Expand Up @@ -104,3 +111,26 @@ export type BookerPlatformWrapperAtomPropsForTeam = BookerPlatformWrapperAtomPro
routingFormSearchParams?: RoutingFormSearchParams;
rrHostSubsetIds?: number[];
};

type SlotInfo = {
time: string;
attendees?: number;
bookingUid?: string;
away?: boolean;
fromUser?: {
id: string;

Choose a reason for hiding this comment

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

🚨 Bug: fromUser.id: string contradicts source IFromUser.id: number

The SlotInfo.fromUser.id field is typed as string, but the source of truth IFromUser interface in packages/features/availability/lib/getUserAvailability.ts defines id: number. The slot data flows from getUserAvailabilityslots.ts → the API response, and at every step the fromUser.id is a numeric user ID from the database.

Interestingly, toUser.id on the very next line in the same SlotInfo type is correctly typed as number, making this an internal inconsistency. This will cause consumers of the atoms package to expect a string where a number is actually returned, leading to subtle bugs (e.g., fromUser.id === someStringId would fail with strict equality).

Was this helpful? React with 👍 / 👎

Suggested change
id: string;
id: number;
  • Apply suggested fix

displayName: string | null;
};
toUser?: {
id: number;
username: string | null;
displayName: string | null;
};
reason?: string;
emoji?: string;
showNotePublicly?: boolean;
};

export type GetAvailableSlotsResponse = {
slots: Record<string, SlotInfo[]>;
};
50 changes: 50 additions & 0 deletions packages/platform/atoms/hooks/schedules/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
type Schedule = {
id: number;
userId: number;
name: string;
timeZone: string | null;
};

export type CreateScheduleHandlerReturn = {
schedule: Schedule;
};

export type DuplicateScheduleHandlerReturn = {
schedule: Schedule;
};

export type GetAvailabilityListHandlerReturn = {
schedules: (Omit<Schedule, "userId"> & {
availability: {
id: number;
userId: number | null;
eventTypeId: number | null;
days: string[];

Choose a reason for hiding this comment

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

🚨 Bug: days: string[] contradicts Prisma schema days Int[]

The Prisma schema defines Availability.days as Int[] (see packages/prisma/schema.prisma), and every other usage in the codebase types days as number[] (e.g., packages/lib/schedules/transformers/getScheduleListItemData.ts, packages/types/schedule.d.ts, packages/platform/atoms/availability/AvailabilitySettings.tsx, packages/platform/atoms/availability/types.ts, packages/features/availability/lib/getUserAvailability.ts).

Changing this to string[] in the atoms package creates a type mismatch between what the API/database actually returns (integer arrays like [0, 1, 2, 3, 4] for weekdays) and what this type declares. This will cause TypeScript compilation errors or silent type unsafety at the boundary where this data is consumed.

Was this helpful? React with 👍 / 👎

Suggested change
days: string[];
days: number[];
  • Apply suggested fix

startTime: Date;
endTime: Date;
date: Date | null;
scheduleId: number | null;
}[];
isDefault: boolean;
})[];
};

export type CreateScheduleInput = {
name: string;
schedule?: { start: Date; end: Date }[][];
eventTypeId?: number;
};

export function validateCreateScheduleInput(input: unknown): CreateScheduleInput {

Choose a reason for hiding this comment

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

⚠️ Edge Case: validateCreateScheduleInput doesn't validate schedule or eventTypeId

The validation function only checks that name is a non-empty string, then casts the entire object to CreateScheduleInput via as. This means the schedule and eventTypeId fields are completely unvalidated — a caller could pass { name: "foo", schedule: "not-an-array", eventTypeId: "not-a-number" } and it would be returned as a valid CreateScheduleInput.

The original implementation used a Zod schema (ZCreateInputSchema) that validated all fields including the nested schedule array structure (z.array(z.array(z.object({ start: z.date(), end: z.date() })))) and eventTypeId as z.number().optional().

Since this function's purpose is to provide runtime validation for external npm package consumers, the unsafe as cast defeats that goal. Consider either:

  1. Adding validation for schedule and eventTypeId fields
  2. Documenting that only name is validated
  3. Using a schema validation library (the atoms package could add a lightweight one)

Was this helpful? React with 👍 / 👎

  • Apply suggested fix

if (!input || typeof input !== 'object') {
throw new Error('Invalid input: must be an object');
}

const data = input as Record<string, unknown>;

if (typeof data.name !== 'string' || data.name.length === 0) {
throw new Error('Invalid input: name must be a non-empty string');
}

return data as CreateScheduleInput;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";

import { SUCCESS_STATUS } from "@calcom/platform-constants";
import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types";
import type { CreateScheduleHandlerReturn } from "@calcom/trpc/server/routers/viewer/availability/schedule/create.handler";
import { TCreateInputSchema as CreateScheduleSchema } from "@calcom/trpc/server/routers/viewer/availability/schedule/create.schema";

import http from "../../lib/http";
import type { CreateScheduleHandlerReturn, CreateScheduleInput as CreateScheduleSchema } from "./types";
import { QUERY_KEY as SchedulesQueryKey } from "./useAtomGetAllSchedules";
import { QUERY_KEY as ScheduleQueryKey } from "./useAtomSchedule";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";

import { SUCCESS_STATUS } from "@calcom/platform-constants";
import type { ApiResponse, ApiErrorResponse } from "@calcom/platform-types";
import type { DuplicateScheduleHandlerReturn } from "@calcom/trpc/server/routers/viewer/availability/schedule/duplicate.handler";

import http from "../../lib/http";
import type { DuplicateScheduleHandlerReturn } from "./types";
import { QUERY_KEY as ScheduleQueryKey } from "./useAtomSchedule";

interface useAtomDuplicateScheduleOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useQuery } from "@tanstack/react-query";

import { SUCCESS_STATUS } from "@calcom/platform-constants";
import type { ApiResponse } from "@calcom/platform-types";
import type { GetAvailabilityListHandlerReturn } from "@calcom/trpc/server/routers/viewer/availability/list.handler";

import http from "../../lib/http";
import type { GetAvailabilityListHandlerReturn } from "./types";
import { useAtomsContext } from "../useAtomsContext";

export const QUERY_KEY = "use-atom-user-schedules";
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/atoms/hooks/useAvailableSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import type {
ApiResponse,
ApiSuccessResponse,
} from "@calcom/platform-types";
import type { GetAvailableSlotsResponse } from "@calcom/trpc/server/routers/viewer/slots/util";

import http from "../lib/http";
import type { GetAvailableSlotsResponse } from "../booker/types";

export const QUERY_KEY = "get-available-slots";

Expand Down
2 changes: 1 addition & 1 deletion packages/platform/atoms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
},
"./add-members-switch/AddMembersWithSwitchPlatformWrapper": "./add-members-switch/AddMembersWithSwitchPlatformWrapper.tsx",
"./availability/AvailabilitySettings": "./availability/AvailabilitySettings.tsx",
"./booker": "./booker/index.ts",
"./booker/types": "./booker/types.ts",

Choose a reason for hiding this comment

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

⚠️ Bug: Package export changed from ./booker to ./booker/types (breaking)

The package.json exports map was changed from "./booker": "./booker/index.ts" to "./booker/types": "./booker/types.ts". This is a breaking change for any external consumer of the @calcom/atoms npm package who imports from @calcom/atoms/booker.

While the test file in this repo (apps/web/test/lib/getSchedule/expects.ts) was updated to use the new path @calcom/atoms/booker/types, external consumers won't get that update automatically. Since @calcom/atoms is a published npm package (currently at v2.2.0), this would require a major version bump per semver conventions.

Note: If the ./booker export was only used internally and the main "." export already re-exports the booker module, this may be less impactful. But as written, this removes a previously available import path.

Was this helpful? React with 👍 / 👎

  • Apply suggested fix

"./selected-calendars/SelectedCalendarsSettings": "./selected-calendars/SelectedCalendarsSettings.tsx",
"./components/ui/shell": "./src/components/ui/shell.tsx",
"./dist/index.ts": "./index.ts",
Expand Down
2 changes: 0 additions & 2 deletions packages/platform/atoms/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
"../../dayjs",
"../../prisma/zod-utils.ts",
"../../prisma/zod",
"../../trpc/server/routers/viewer/slots",
"../../trpc/server/types",
"../../features/eventtypes",
"../../features/schedules",
"../../features/bookings/Booker",
Expand Down
8 changes: 0 additions & 8 deletions packages/trpc/server/routers/viewer/slots/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,6 @@ export const reserveSlotSchema = z
"Either slotUtcStartDate, slotUtcEndDate or eventTypeId should be filled in."
);

export type Slot = {
time: string;
userIds?: number[];
attendees?: number;
bookingUid?: string;
users?: string[];
};

export const removeSelectedSlotSchema = z.object({
uid: z.string().nullable(),
});
Expand Down