import type { Person } from "./types"; export const clean = (value: unknown, max = 120) => String(value || "").trim().slice(0, max); export const normalizeEmail = (value: unknown) => clean(value, 254).toLowerCase(); export function isSameOrigin(request: Request): boolean { const origin = request.headers.get("origin"); if (!origin) return true; try { const originUrl = new URL(origin); const host = request.headers.get("x-forwarded-host") || request.headers.get("host"); const protocol = request.headers.get("x-forwarded-proto"); return originUrl.host === host && (!protocol || originUrl.protocol === `${protocol}:`); } catch { return false; } } export function isSecureRequest(request: Request): boolean { const protocol = request.headers.get("x-forwarded-proto"); return protocol ? protocol === "https" : new URL(request.url).protocol === "https:"; } export function isUnder18(birthday: string): boolean { const slashDate = birthday.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/); const date = slashDate ? new Date(Number(slashDate[3]), Number(slashDate[1]) - 1, Number(slashDate[2])) : new Date(`${birthday}T00:00:00`); if (Number.isNaN(date.getTime())) return false; const today = new Date(); let age = today.getFullYear() - date.getFullYear(); const beforeBirthday = today.getMonth() < date.getMonth() || (today.getMonth() === date.getMonth() && today.getDate() < date.getDate()); if (beforeBirthday) age--; return age >= 0 && age < 18; } function isValidBirthday(birthday: string): boolean { if (!birthday) return true; const monthDay = birthday.match(/^(\d{1,2})\/(\d{1,2})(?:\/(\d{4}))?$/); if (monthDay) { const month = Number(monthDay[1]); const day = Number(monthDay[2]); const year = Number(monthDay[3] || 2000); const date = new Date(year, month - 1, day); return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day; } return /^\d{4}-\d{2}-\d{2}$/.test(birthday) && !Number.isNaN(new Date(`${birthday}T00:00:00`).getTime()); } export function parsePeople(value: unknown, children = false): Person[] { if (!Array.isArray(value)) return []; return value.slice(0, 20).map((raw, index) => { const person = raw as Record; const birthday = clean(person.birthday, 10); if (!isValidBirthday(birthday)) throw new Error(`Person ${index + 1} has an invalid birthday.`); const hasBirthYear = /^\d{4}-/.test(birthday) || /^\d{1,2}\/\d{1,2}\/\d{4}$/.test(birthday); if (children && hasBirthYear && !isUnder18(birthday)) throw new Error(`Child ${index + 1} must be under 18.`); return { id: clean(person.id, 50) || crypto.randomUUID(), firstName: clean(person.firstName, 60), lastName: clean(person.lastName, 60), email: normalizeEmail(person.email) || undefined, phone: clean(person.phone, 30) || undefined, birthday: birthday || undefined, photoId: clean(person.photoId, 50) || undefined, }; }).filter((person) => person.firstName && person.lastName); }