refactor: use explicit type annotations for TUpdateInputSchema to reduce type inference complexity (#26133)

* refactor: use explicit type annotations for TUpdateInputSchema to reduce type inference complexity

This change significantly reduces TypeScript type-checking time by:
1. Defining explicit TypeScript types instead of using z.infer<> on complex schema chains
2. Using z.ZodType<T> annotations on schemas to prevent TypeScript from inferring through .extend(), .partial(), .merge() chains
3. Removing the dependency on EventTypeSchema from @calcom/prisma/zod/modelSchema which pulls in many dependencies

The schema still validates all fields at runtime - only the compile-time type inference is optimized.

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: update BookingFieldInput and users types to fix type errors

- BookingFieldInput now includes name, hidden, required properties with index signature
- users type changed from (string | number)[] to number[]
- Zod schema updated to use z.array(z.number()) for users field
- Removed unused stringOrNumber import

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: handle bookingFields null case with Prisma.DbNull for JSON field compatibility

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: remove index signature from BookingFieldInput for API v2 DTO compatibility

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* chore: remove useless JSDoc comments per PR feedback

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* test: add unit tests for aiPhoneCallConfig transformation in ZUpdateInputSchema

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* test: add unit tests for bookingFields null to Prisma.DbNull transformation

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* chore: remove comments from update.handler.test.ts

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* refactor: scope test describe block to update.handler per PR feedback

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Keith Williams
2025-12-29 07:40:49 -03:00
committed by GitHub
co-authored by Devin AI
parent 55805f081d
commit 4128a9b4fb
4 changed files with 568 additions and 62 deletions
@@ -0,0 +1,142 @@
import { describe, it, expect } from "vitest";
import { ZUpdateInputSchema } from "../types";
describe("ZUpdateInputSchema", () => {
describe("aiPhoneCallConfig transformation", () => {
const baseValidInput = {
id: 1,
};
const baseAiPhoneCallConfig = {
generalPrompt: "Test prompt",
enabled: true,
beginMessage: "Hello",
yourPhoneNumber: "+1234567890",
numberToCall: "+0987654321",
templateType: "CUSTOM_TEMPLATE" as const,
};
it("should pass validation with valid aiPhoneCallConfig", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: baseAiPhoneCallConfig,
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
});
it("should set yourPhoneNumber to empty string when falsy", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: {
...baseAiPhoneCallConfig,
yourPhoneNumber: "",
},
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig?.yourPhoneNumber).toBe("");
}
});
it("should set numberToCall to empty string when falsy", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: {
...baseAiPhoneCallConfig,
numberToCall: "",
},
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig?.numberToCall).toBe("");
}
});
it("should set guestName to undefined when null", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: {
...baseAiPhoneCallConfig,
guestName: null,
},
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig?.guestName).toBeUndefined();
}
});
it("should set guestEmail to null when undefined", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: {
...baseAiPhoneCallConfig,
guestEmail: undefined,
},
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig?.guestEmail).toBeNull();
}
});
it("should set guestCompany to null when undefined", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: {
...baseAiPhoneCallConfig,
guestCompany: undefined,
},
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig?.guestCompany).toBeNull();
}
});
it("should preserve existing values when provided", () => {
const input = {
...baseValidInput,
aiPhoneCallConfig: {
...baseAiPhoneCallConfig,
guestName: "John Doe",
guestEmail: "john@example.com",
guestCompany: "Acme Inc",
},
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig?.guestName).toBe("John Doe");
expect(result.data.aiPhoneCallConfig?.guestEmail).toBe("john@example.com");
expect(result.data.aiPhoneCallConfig?.guestCompany).toBe("Acme Inc");
}
});
it("should allow undefined aiPhoneCallConfig", () => {
const input = {
...baseValidInput,
};
const result = ZUpdateInputSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.aiPhoneCallConfig).toBeUndefined();
}
});
});
});
@@ -0,0 +1,59 @@
import { describe, it, expect } from "vitest";
import { Prisma } from "@calcom/prisma/client";
describe("update.handler", () => {
describe("bookingFields null to Prisma.DbNull transformation", () => {
function transformBookingFields(
bookingFields: null | undefined | Prisma.InputJsonValue
): typeof Prisma.DbNull | Prisma.InputJsonValue | undefined {
return bookingFields === null ? Prisma.DbNull : (bookingFields as Prisma.InputJsonValue | undefined);
}
it("should convert null to Prisma.DbNull", () => {
const result = transformBookingFields(null);
expect(result).toBe(Prisma.DbNull);
});
it("should pass through undefined as-is", () => {
const result = transformBookingFields(undefined);
expect(result).toBeUndefined();
});
it("should pass through an array of booking fields as-is", () => {
const bookingFieldsArray = [
{
name: "email",
type: "email",
label: "Email",
required: true,
hidden: false,
},
{
name: "name",
type: "name",
label: "Name",
required: true,
hidden: false,
},
];
const result = transformBookingFields(bookingFieldsArray);
expect(result).toEqual(bookingFieldsArray);
});
it("should pass through an empty array as-is", () => {
const result = transformBookingFields([]);
expect(result).toEqual([]);
});
it("should distinguish between null and empty array", () => {
const nullResult = transformBookingFields(null);
const emptyArrayResult = transformBookingFields([]);
expect(nullResult).toBe(Prisma.DbNull);
expect(emptyArrayResult).toEqual([]);
expect(nullResult).not.toEqual(emptyArrayResult);
});
});
});
@@ -240,7 +240,8 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
}),
description: newDescription,
title: newTitle,
bookingFields,
bookingFields:
bookingFields === null ? Prisma.DbNull : (bookingFields as Prisma.InputJsonValue | undefined),
maxActiveBookingsPerBooker,
isRRWeightsEnabled,
rrSegmentQueryValue:
@@ -1,18 +1,245 @@
import { z } from "zod";
import type { TemplateType } from "@calcom/features/calAIPhone/zod-utils";
import { templateTypeEnum } from "@calcom/features/calAIPhone/zod-utils";
import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/lib/constants";
import type { PeriodType, SchedulingType } from "@calcom/prisma/enums";
import type {
CustomInputSchema,
EventTypeMetadata,
IntervalLimit,
EventTypeLocation,
} from "@calcom/prisma/zod-utils";
import {
customInputSchema,
EventTypeMetaDataSchema,
stringOrNumber,
rrSegmentQueryValueSchema,
eventTypeLocations,
recurringEventType,
intervalLimitsType,
eventTypeBookingFields,
eventTypeColor,
} from "@calcom/prisma/zod-utils";
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import { DestinationCalendarSchema } from "@calcom/prisma/zod/modelSchema/DestinationCalendarSchema";
import { EventTypeSchema } from "@calcom/prisma/zod/modelSchema/EventTypeSchema";
const hashedLinkInputSchema = z
// ============================================================================
// EXPLICIT TYPE DEFINITIONS
// ============================================================================
// These explicit types prevent TypeScript from having to infer complex types
// through chains of .extend(), .partial(), .merge() etc. This significantly
// reduces type-checking time by giving TypeScript the answer directly.
// ============================================================================
type HashedLinkInput = {
link: string;
expiresAt?: Date | null;
maxUsageCount?: number | null;
usageCount?: number | null;
};
type AiPhoneCallConfig = {
generalPrompt: string;
enabled: boolean;
beginMessage: string | null;
yourPhoneNumber: string;
numberToCall: string;
guestName?: string | null;
guestEmail?: string | null;
guestCompany?: string | null;
templateType: TemplateType;
};
type CalVideoSettings = {
disableRecordingForGuests?: boolean | null;
disableRecordingForOrganizer?: boolean | null;
enableAutomaticTranscription?: boolean | null;
enableAutomaticRecordingForOrganizer?: boolean | null;
disableTranscriptionForGuests?: boolean | null;
disableTranscriptionForOrganizer?: boolean | null;
redirectUrlOnExit?: string | null;
requireEmailForGuests?: boolean | null;
} | null;
type HostInput = {
userId: number;
profileId?: number | null;
isFixed?: boolean;
priority?: number | null;
weight?: number | null;
scheduleId?: number | null;
groupId?: string | null;
};
type HostGroupInput = {
id: string;
name: string;
};
type ChildInput = {
owner: {
id: number;
name: string;
email: string;
eventTypeSlugs: string[];
};
hidden: boolean;
};
type DestinationCalendarInput = {
integration: string;
externalId: string;
} | null;
type RecurringEventInput = {
dtstart?: Date;
interval: number;
count: number;
freq: number;
until?: Date;
tzid?: string;
} | null;
type EventTypeColorInput = {
lightEventTypeColor: string;
darkEventTypeColor: string;
} | null;
/**
* Booking field type - minimal type definition for fields that need to be accessed.
* Only includes properties that are actually read in server code.
* Does NOT use an index signature to maintain compatibility with API v2 DTO classes.
*/
type BookingFieldInput = {
name: string;
hidden?: boolean;
required?: boolean;
type?: string;
};
/**
* RR Segment query value - using index signature for complex RAQB structure.
* The values need to be indexable (string keys) for downstream usage.
*/
type RRSegmentQueryValueInput = {
[key: string]: unknown;
} | null;
/**
* Explicit type definition for event type update input.
*
* This type is defined explicitly rather than using z.infer<> on a complex
* schema chain to significantly reduce TypeScript type-checking time.
* The schema still validates all fields at runtime.
*
* All fields are optional (from .partial()) except `id` which is required.
*/
export type TUpdateInputSchema = {
// Required field
id: number;
// Fields from EventTypeSchema (all optional due to .partial())
periodType?: PeriodType;
schedulingType?: SchedulingType | null;
title?: string;
slug?: string;
description?: string | null;
interfaceLanguage?: string | null;
position?: number;
locations?: EventTypeLocation[] | null;
length?: number;
offsetStart?: number;
hidden?: boolean;
userId?: number | null;
profileId?: number | null;
teamId?: number | null;
useEventLevelSelectedCalendars?: boolean;
eventName?: string | null;
parentId?: number | null;
bookingFields?: BookingFieldInput[] | null;
timeZone?: string | null;
periodStartDate?: Date | null;
periodEndDate?: Date | null;
periodDays?: number | null;
periodCountCalendarDays?: boolean | null;
lockTimeZoneToggleOnBookingPage?: boolean;
lockedTimeZone?: string | null;
requiresConfirmation?: boolean;
requiresConfirmationWillBlockSlot?: boolean;
requiresConfirmationForFreeEmail?: boolean;
requiresBookerEmailVerification?: boolean;
canSendCalVideoTranscriptionEmails?: boolean;
autoTranslateDescriptionEnabled?: boolean;
autoTranslateInstantMeetingTitleEnabled?: boolean;
recurringEvent?: RecurringEventInput;
disableGuests?: boolean;
hideCalendarNotes?: boolean;
hideCalendarEventDetails?: boolean;
minimumBookingNotice?: number;
beforeEventBuffer?: number;
afterEventBuffer?: number;
seatsPerTimeSlot?: number | null;
onlyShowFirstAvailableSlot?: boolean;
showOptimizedSlots?: boolean | null;
disableCancelling?: boolean | null;
disableRescheduling?: boolean | null;
minimumRescheduleNotice?: number | null;
seatsShowAttendees?: boolean | null;
seatsShowAvailabilityCount?: boolean | null;
scheduleId?: number | null;
allowReschedulingCancelledBookings?: boolean | null;
price?: number;
currency?: string;
slotInterval?: number | null;
metadata?: EventTypeMetadata;
successRedirectUrl?: string | null;
forwardParamsSuccessRedirect?: boolean | null;
bookingLimits?: IntervalLimit | null;
durationLimits?: IntervalLimit | null;
isInstantEvent?: boolean;
instantMeetingExpiryTimeOffsetInSeconds?: number;
instantMeetingScheduleId?: number | null;
instantMeetingParameters?: string[];
assignAllTeamMembers?: boolean;
assignRRMembersUsingSegment?: boolean;
rrSegmentQueryValue?: RRSegmentQueryValueInput;
useEventTypeDestinationCalendarEmail?: boolean;
isRRWeightsEnabled?: boolean;
maxLeadThreshold?: number | null;
includeNoShowInRRCalculation?: boolean;
allowReschedulingPastBookings?: boolean;
hideOrganizerEmail?: boolean;
maxActiveBookingsPerBooker?: number | null;
maxActiveBookingPerBookerOfferReschedule?: boolean;
customReplyToEmail?: string | null;
eventTypeColor?: EventTypeColorInput;
rescheduleWithSameRoundRobinHost?: boolean;
secondaryEmailId?: number | null;
useBookerTimezone?: boolean;
restrictionScheduleId?: number | null;
bookingRequiresAuthentication?: boolean;
rrHostSubsetEnabled?: boolean;
createdAt?: Date | null;
updatedAt?: Date | null;
// Extended fields (all optional due to .partial())
aiPhoneCallConfig?: AiPhoneCallConfig;
calVideoSettings?: CalVideoSettings;
calAiPhoneScript?: string;
customInputs?: CustomInputSchema[];
destinationCalendar?: DestinationCalendarInput;
users?: number[];
children?: ChildInput[];
hosts?: HostInput[];
schedule?: number | null;
instantMeetingSchedule?: number | null;
multiplePrivateLinks?: (string | HashedLinkInput)[];
hostGroups?: HostGroupInput[];
};
// ============================================================================
// ZOD SCHEMAS
// ============================================================================
const hashedLinkInputSchema: z.ZodType<HashedLinkInput> = z
.object({
link: z.string(),
expiresAt: z.date().nullish(),
@@ -21,7 +248,7 @@ const hashedLinkInputSchema = z
})
.strict();
const aiPhoneCallConfig = z
const aiPhoneCallConfigSchema: z.ZodType<AiPhoneCallConfig | undefined> = z
.object({
generalPrompt: z.string(),
enabled: z.boolean(),
@@ -35,7 +262,7 @@ const aiPhoneCallConfig = z
})
.optional();
const calVideoSettingsSchema = z
const calVideoSettingsSchema: z.ZodType<CalVideoSettings | undefined> = z
.object({
disableRecordingForGuests: z.boolean().nullish(),
disableRecordingForOrganizer: z.boolean().nullish(),
@@ -49,7 +276,7 @@ const calVideoSettingsSchema = z
.optional()
.nullable();
const hostSchema = z.object({
const hostSchema: z.ZodType<HostInput> = z.object({
userId: z.number(),
profileId: z.number().or(z.null()).optional(),
isFixed: z.boolean().optional(),
@@ -59,12 +286,12 @@ const hostSchema = z.object({
groupId: z.string().optional().nullable(),
});
const hostGroupSchema = z.object({
const hostGroupSchema: z.ZodType<HostGroupInput> = z.object({
id: z.string().uuid(),
name: z.string(),
});
const childSchema = z.object({
const childSchema: z.ZodType<ChildInput> = z.object({
owner: z.object({
id: z.number(),
name: z.string(),
@@ -74,55 +301,132 @@ const childSchema = z.object({
hidden: z.boolean(),
});
/** Optional fields */
const BaseEventTypeUpdateInput = EventTypeSchema.extend({
isInstantEvent: z.boolean(),
instantMeetingParameters: z.array(z.string()),
instantMeetingExpiryTimeOffsetInSeconds: z.number(),
aiPhoneCallConfig,
calVideoSettings: calVideoSettingsSchema,
calAiPhoneScript: z.string(),
customInputs: z.array(customInputSchema),
destinationCalendar: DestinationCalendarSchema
.pick({
integration: true,
externalId: true,
})
.nullable(),
users: z.array(stringOrNumber),
children: z.array(childSchema),
hosts: z.array(hostSchema),
schedule: z.number().nullable(),
instantMeetingSchedule: z.number().nullable(),
multiplePrivateLinks: z.array(z.union([z.string(), hashedLinkInputSchema])),
assignAllTeamMembers: z.boolean(),
isRRWeightsEnabled: z.boolean(),
metadata: EventTypeMetaDataSchema,
bookingFields: eventTypeBookingFields,
assignRRMembersUsingSegment: z.boolean().optional(),
rrSegmentQueryValue: rrSegmentQueryValueSchema.optional(),
useEventLevelSelectedCalendars: z.boolean().optional(),
seatsPerTimeSlot: z.number().min(1).max(MAX_SEATS_PER_TIME_SLOT).nullable().optional(),
hostGroups: z.array(hostGroupSchema).optional(),
})
.partial()
.extend(EventTypeSchema.pick({ id: true }).shape);
const destinationCalendarInputSchema: z.ZodType<DestinationCalendarInput> = z
.object({
integration: z.string(),
externalId: z.string(),
})
.nullable();
export const ZUpdateInputSchema = BaseEventTypeUpdateInput.extend({
aiPhoneCallConfig: aiPhoneCallConfig.refine(
(data) => {
if (!data) return true;
data.yourPhoneNumber = data.yourPhoneNumber || "";
data.numberToCall = data.numberToCall || "";
data.guestName = data.guestName ?? undefined;
data.guestEmail = data.guestEmail ?? null;
data.guestCompany = data.guestCompany ?? null;
return true;
},
{
message: "Applying default values and transformations",
}
),
}).strict();
// only run infer over the simple type, excluding refines/transforms.
export type TUpdateInputSchema = z.infer<typeof BaseEventTypeUpdateInput>;
/**
* Base schema for event type updates.
*
* Uses z.ZodType<TUpdateInputSchema> annotation to prevent TypeScript from
* inferring the complex type through the schema chain. This is a key
* optimization that reduces type-checking time significantly.
*/
const BaseEventTypeUpdateInput: z.ZodType<TUpdateInputSchema> = z
.object({
// Required field
id: z.number().int(),
// Fields from EventTypeSchema
periodType: z.enum(["UNLIMITED", "ROLLING", "ROLLING_WINDOW", "RANGE"]).optional(),
schedulingType: z.enum(["ROUND_ROBIN", "COLLECTIVE", "MANAGED"]).nullable().optional(),
title: z.string().min(1).optional(),
slug: z.string().optional(),
description: z.string().nullable().optional(),
interfaceLanguage: z.string().nullable().optional(),
position: z.number().int().optional(),
locations: eventTypeLocations.nullable().optional(),
length: z.number().min(1).optional(),
offsetStart: z.number().int().optional(),
hidden: z.boolean().optional(),
userId: z.number().int().nullable().optional(),
profileId: z.number().int().nullable().optional(),
teamId: z.number().int().nullable().optional(),
useEventLevelSelectedCalendars: z.boolean().optional(),
eventName: z.string().nullable().optional(),
parentId: z.number().int().nullable().optional(),
bookingFields: eventTypeBookingFields.nullable().optional(),
timeZone: z.string().nullable().optional(),
periodStartDate: z.coerce.date().nullable().optional(),
periodEndDate: z.coerce.date().nullable().optional(),
periodDays: z.number().int().nullable().optional(),
periodCountCalendarDays: z.boolean().nullable().optional(),
lockTimeZoneToggleOnBookingPage: z.boolean().optional(),
lockedTimeZone: z.string().nullable().optional(),
requiresConfirmation: z.boolean().optional(),
requiresConfirmationWillBlockSlot: z.boolean().optional(),
requiresConfirmationForFreeEmail: z.boolean().optional(),
requiresBookerEmailVerification: z.boolean().optional(),
canSendCalVideoTranscriptionEmails: z.boolean().optional(),
autoTranslateDescriptionEnabled: z.boolean().optional(),
autoTranslateInstantMeetingTitleEnabled: z.boolean().optional(),
recurringEvent: recurringEventType.nullable().optional(),
disableGuests: z.boolean().optional(),
hideCalendarNotes: z.boolean().optional(),
hideCalendarEventDetails: z.boolean().optional(),
minimumBookingNotice: z.number().min(0).optional(),
beforeEventBuffer: z.number().int().optional(),
afterEventBuffer: z.number().int().optional(),
seatsPerTimeSlot: z.number().min(1).max(MAX_SEATS_PER_TIME_SLOT).nullable().optional(),
onlyShowFirstAvailableSlot: z.boolean().optional(),
showOptimizedSlots: z.boolean().nullable().optional(),
disableCancelling: z.boolean().nullable().optional(),
disableRescheduling: z.boolean().nullable().optional(),
minimumRescheduleNotice: z.number().min(0).nullable().optional(),
seatsShowAttendees: z.boolean().nullable().optional(),
seatsShowAvailabilityCount: z.boolean().nullable().optional(),
scheduleId: z.number().int().nullable().optional(),
allowReschedulingCancelledBookings: z.boolean().nullable().optional(),
price: z.number().int().optional(),
currency: z.string().optional(),
slotInterval: z.number().int().nullable().optional(),
metadata: EventTypeMetaDataSchema.optional(),
successRedirectUrl: z.string().nullable().optional(),
forwardParamsSuccessRedirect: z.boolean().nullable().optional(),
bookingLimits: intervalLimitsType.nullable().optional(),
durationLimits: intervalLimitsType.nullable().optional(),
isInstantEvent: z.boolean().optional(),
instantMeetingExpiryTimeOffsetInSeconds: z.number().int().optional(),
instantMeetingScheduleId: z.number().int().nullable().optional(),
instantMeetingParameters: z.string().array().optional(),
assignAllTeamMembers: z.boolean().optional(),
assignRRMembersUsingSegment: z.boolean().optional(),
rrSegmentQueryValue: rrSegmentQueryValueSchema.nullable().optional(),
useEventTypeDestinationCalendarEmail: z.boolean().optional(),
isRRWeightsEnabled: z.boolean().optional(),
maxLeadThreshold: z.number().int().nullable().optional(),
includeNoShowInRRCalculation: z.boolean().optional(),
allowReschedulingPastBookings: z.boolean().optional(),
hideOrganizerEmail: z.boolean().optional(),
maxActiveBookingsPerBooker: z.number().int().nullable().optional(),
maxActiveBookingPerBookerOfferReschedule: z.boolean().optional(),
customReplyToEmail: z.string().nullable().optional(),
eventTypeColor: eventTypeColor.nullable().optional(),
rescheduleWithSameRoundRobinHost: z.boolean().optional(),
secondaryEmailId: z.number().int().nullable().optional(),
useBookerTimezone: z.boolean().optional(),
restrictionScheduleId: z.number().int().nullable().optional(),
bookingRequiresAuthentication: z.boolean().optional(),
rrHostSubsetEnabled: z.boolean().optional(),
createdAt: z.coerce.date().nullable().optional(),
updatedAt: z.coerce.date().nullable().optional(),
// Extended fields
aiPhoneCallConfig: aiPhoneCallConfigSchema,
calVideoSettings: calVideoSettingsSchema,
calAiPhoneScript: z.string().optional(),
customInputs: z.array(customInputSchema).optional(),
destinationCalendar: destinationCalendarInputSchema.optional(),
users: z.array(z.number()).optional(),
children: z.array(childSchema).optional(),
hosts: z.array(hostSchema).optional(),
schedule: z.number().nullable().optional(),
instantMeetingSchedule: z.number().nullable().optional(),
multiplePrivateLinks: z.array(z.union([z.string(), hashedLinkInputSchema])).optional(),
hostGroups: z.array(hostGroupSchema).optional(),
})
.strict();
export const ZUpdateInputSchema = BaseEventTypeUpdateInput.superRefine((data, _ctx) => {
// Apply transformations to aiPhoneCallConfig if present
if (data.aiPhoneCallConfig) {
data.aiPhoneCallConfig.yourPhoneNumber = data.aiPhoneCallConfig.yourPhoneNumber || "";
data.aiPhoneCallConfig.numberToCall = data.aiPhoneCallConfig.numberToCall || "";
data.aiPhoneCallConfig.guestName = data.aiPhoneCallConfig.guestName ?? undefined;
data.aiPhoneCallConfig.guestEmail = data.aiPhoneCallConfig.guestEmail ?? null;
data.aiPhoneCallConfig.guestCompany = data.aiPhoneCallConfig.guestCompany ?? null;
}
});