import {z} from 'zod'; // Error messages are i18n keys — translate them in components via t(error.message) const emailField = z .string() .min(1, 'VALIDATION.EMAIL_REQUIRED') .email('VALIDATION.EMAIL_INVALID'); // Auth hashes with BCrypt, which takes at most 72 bytes. const passwordField = z .string() .min(8, 'VALIDATION.PASSWORD_TOO_SHORT') .refine((value) => new TextEncoder().encode(value).length <= 72, 'VALIDATION.PASSWORD_TOO_LONG'); // Same rule as the Auth service: 3-24 letters, digits, space, _ . - and no space at the ends. // An empty field means "no display name" and is sent as null. const displayNameField = z .string() .trim() .optional() .transform((v) => (v ? v : null)) .refine((v) => v === null || /^[\p{L}\p{N}_.-][\p{L}\p{N} _.-]{1,22}[\p{L}\p{N}_.-]$/u.test(v), { message: 'VALIDATION.DISPLAY_NAME_INVALID', }); // No password: the address owner chooses it on the confirm page. export const registrationSchema = z.object({ email: emailField, displayName: displayNameField, }); export const confirmEmailSchema = z .object({ password: passwordField, passwordConfirmation: z.string().min(1, 'VALIDATION.PASSWORD_CONFIRMATION_REQUIRED'), }) .refine((d) => d.password === d.passwordConfirmation, { path: ['passwordConfirmation'], message: 'VALIDATION.PASSWORD_MISMATCH', }); export const settingsSchema = z.object({ displayName: displayNameField, });