import { z } from "zod"; export const displayNameSchema = z .string() .trim() .min(2, "Use at least 2 characters") .max(32, "Use at most 32 characters") .refine( (value) => !/[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069]/u.test(value), "Use a single line without hidden control characters", ); export const newPasswordSchema = z .string() .min(12, "Use at least 12 characters") .refine( (value) => new TextEncoder().encode(value).length <= 72, "Use at most 72 UTF-8 bytes for your password", ); const email = z.string().trim().email().max(254); export const authActionSchema = z.discriminatedUnion("action", [ z .object({ action: z.literal("login"), email, password: z.string().min(1).max(1024), next: z.string().optional(), }) .strict(), z .object({ action: z.literal("signup"), email, password: newPasswordSchema, displayName: displayNameSchema, next: z.string().optional(), }) .strict(), z.object({ action: z.literal("recover"), email }).strict(), z .object({ action: z.literal("magic"), email, next: z.string().optional() }) .strict(), ]); export const profileUpdateSchema = z .object({ displayName: displayNameSchema }) .strict(); export const passwordUpdateSchema = z .object({ password: newPasswordSchema, currentPassword: z.string().min(1).max(1024).optional(), nonce: z .string() .trim() .regex(/^[0-9]{6,10}$/) .optional(), }) .strict(); /** Only supported product destinations can follow an authentication link. */ export function authDestination(value: string | null | undefined): string { if ( value === "/settings" || value === "/dashboard" || value === "/auth/reset-password" ) return value; if (value && /^\/dashboard\/[0-9a-f-]{36}$/i.test(value)) return value; if (value && /^\/pair\?code=[A-F0-9]{8}$/.test(value)) return value; return "/dashboard"; }