diff --git a/src/lib/types.ts b/src/lib/types.ts index 4f63518..a50a9a4 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,12 +1,26 @@ import { z } from "zod"; +import { MAX_IMAGE_SIZE_BYTES } from "@/lib/constants"; + +/** Validates that a base64 data URL string decodes to binary under the max size. */ +const isValidImageSize = (val: string | undefined): boolean => { + if (!val) return true; + const base64Part = val.split(",")[1] ?? ""; + const binarySize = Math.ceil(base64Part.length * 0.75); + return binarySize <= MAX_IMAGE_SIZE_BYTES; +}; export const AiEventRequestSchema = z .object({ prompt: z.string().trim().max(2000).optional(), imageBase64: z .string() - .startsWith("data:", "Must be a valid data URL") - .max(10 * 1024 * 1024 * 1.37, "Image must be less than 10MB") + .regex( + /^data:image\/(png|jpeg|webp);base64,/, + "Must be a valid image data URL (PNG, JPEG, or WebP)", + ) + .refine(isValidImageSize, { + message: "Image must be less than 10MB", + }) .optional(), }) .refine((data) => data.prompt || data.imageBase64, { @@ -21,25 +35,18 @@ export const AiEventResponseItemSchema = z.object({ description: z.string().optional(), location: z.string().optional(), url: z.string().optional(), - start: z.string(), - end: z.string().optional(), + start: z.string().datetime({ offset: true }), + end: z.string().datetime({ offset: true }).optional(), allDay: z.boolean().optional(), recurrenceRule: z.string().optional(), }); export const AiEventResponseSchema = z.array(AiEventResponseItemSchema); -export type CalendarEvent = { +export type AiEventResponseItem = z.infer; + +export type CalendarEvent = AiEventResponseItem & { id: string; - title: string; - description?: string; - location?: string; - url?: string; - start: string; - end?: string; - allDay?: boolean; createdAt?: string; lastModified?: string; - - recurrenceRule?: string; };