🐛 fix: normalize AI-generated dates to valid ISO offset datetimes

- add normalizeAiDateString() to coerce bare ISO datetimes, date-only
  strings, and fractional-second variants into offset-aware format
- apply via z.preprocess in AiEventResponseItemSchema so Zod validation
  no longer rejects AI responses missing a timezone offset
- fix system prompt to use toISOString() (unambiguous UTC) and clarify
  expected datetime format for the AI model
- install bun-types and add to tsconfig so bun:test resolves cleanly
- add 8 behaviour-driven tests covering all normalizer edge cases
This commit is contained in:
2026-04-07 23:01:42 -04:00
parent f3ccbf5db5
commit cbd2559169
7 changed files with 109 additions and 5 deletions

View File

@@ -1,5 +1,6 @@
import { z } from "zod";
import { MAX_IMAGE_SIZE_BYTES } from "@/lib/constants";
import { normalizeAiDateString } from "@/lib/date-normalizer";
/** Validates that a base64 data URL string decodes to binary under the max size. */
const isValidImageSize = (val: string | undefined): boolean => {
@@ -29,14 +30,19 @@ export const AiEventRequestSchema = z
export type AiEventRequest = z.infer<typeof AiEventRequestSchema>;
const aiDatetime = z.preprocess(
(val) => (typeof val === "string" ? normalizeAiDateString(val) : val),
z.string().datetime({ offset: true }),
);
export const AiEventResponseItemSchema = z.object({
id: z.string().optional(),
title: z.string().min(1),
description: z.string().optional(),
location: z.string().optional(),
url: z.string().optional(),
start: z.string().datetime({ offset: true }),
end: z.string().datetime({ offset: true }).optional(),
start: aiDatetime,
end: aiDatetime.optional(),
allDay: z.boolean().optional(),
recurrenceRule: z.string().optional(),
});