import { z } from 'zod';
// ============================================
// Primitives
// ============================================
const emailSchema = z.string().email().min(5).max(100).trim().toLowerCase();
const passwordSchema = z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Must contain at least one uppercase letter')
.regex(/[a-z]/, 'Must contain at least one lowercase letter')
.regex(/[0-9]/, 'Must contain at least one number')
.regex(/[^A-Za-z0-9]/, 'Must contain at least one special character');
const idSchema = z.string().uuid();
// ============================================
// Domain Schemas
// ============================================
export const UserSchema = z.object({
id: idSchema,
email: emailSchema,
name: z.string().min(2).max(50),
age: z.number().int().min(13).max(120).optional(),
role: z.enum(['user', 'admin', 'moderator']).default('user'),
isActive: z.boolean().default(true),
tags: z.array(z.string()).max(10),
meta: z.record(z.unknown()).optional(),
createdAt: z.date(),
});
export const LoginRequestSchema = z.object({
email: emailSchema,
password: z.string(), // Don't validate complexity on login, just type
rememberMe: z.boolean().optional(),
});
export const RegisterRequestSchema = z.object({
email: emailSchema,
name: z.string().min(2),
password: passwordSchema,
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ['confirmPassword'],
});
// ============================================
// API Response Schemas
// ============================================
export const ApiResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) => z.object({
success: z.boolean(),
data: dataSchema.optional(),
error: z.object({
code: z.string(),
message: z.string(),
details: z.any().optional(),
}).optional(),
timestamp: z.string().datetime(),
});
// ============================================
// Environment Validation
// ============================================
export const EnvSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
API_KEY: z.string().optional(),
});
// ============================================
// Utility Types (Inferred)
// ============================================
export type User = z.infer<typeof UserSchema>;
export type LoginRequest = z.infer<typeof LoginRequestSchema>;
export type RegisterRequest = z.infer<typeof RegisterRequestSchema>;
export type Env = z.infer<typeof EnvSchema>;
// ============================================
// Error Handler Helper
// ============================================
export const formatZodError = (error: z.ZodError) => {
return error.errors.map(err => ({
path: err.path.join('.'),
message: err.message,
code: err.code
}));
};
// ============================================
// Validation Middleware Example
// ============================================
/*
import { Request, Response, NextFunction } from 'express';
export const validate = (schema: z.ZodSchema) =>
(req: Request, res: Response, next: NextFunction) => {
try {
req.body = schema.parse(req.body);
next();
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
success: false,
errors: formatZodError(error)
});
}
next(error);
}
};
*/