UNPKG

emr-types

Version:

Comprehensive TypeScript Types Library for Electronic Medical Record (EMR) Applications - Domain-Driven Design with Zod Validation

314 lines 11.5 kB
import { z } from 'zod'; import { UserRole, UserStatus } from '../domains/user'; // ============================================================================ // BASE SCHEMAS // ============================================================================ export const UserIdSchema = z.string().uuid().brand(); export const EmailSchema = z.string().email().brand(); export const PasswordSchema = z.string().min(8).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, { message: 'Password must contain at least one lowercase letter, one uppercase letter, and one number' }).brand(); // ============================================================================ // ENUM SCHEMAS // ============================================================================ export const UserRoleSchema = z.nativeEnum(UserRole); export const UserStatusSchema = z.nativeEnum(UserStatus); // ============================================================================ // VALUE OBJECT SCHEMAS // ============================================================================ export const UserIdValueObjectSchema = z.object({ value: UserIdSchema, format: z.enum(['uuid', 'auto-increment', 'custom']), createdAt: z.date(), metadata: z.record(z.unknown()).optional() }); export const EmailValueObjectSchema = z.object({ value: EmailSchema, isVerified: z.boolean(), verifiedAt: z.date().optional(), createdAt: z.date() }); export const PasswordValueObjectSchema = z.object({ value: PasswordSchema, hashedValue: z.string(), salt: z.string(), algorithm: z.enum(['bcrypt', 'argon2', 'scrypt']), iterations: z.number().positive(), createdAt: z.date(), lastChangedAt: z.date() }); // ============================================================================ // ENTITY SCHEMAS // ============================================================================ export const UserProfileSchema = z.object({ id: UserIdSchema, firstName: z.string().min(1).max(50), lastName: z.string().min(1).max(50), displayName: z.string().max(100).optional(), avatar: z.string().url().optional(), bio: z.string().max(500).optional(), dateOfBirth: z.date().optional(), phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/).optional(), address: z.object({ street: z.string().max(200), city: z.string().max(100), state: z.string().max(100), postalCode: z.string().max(20), country: z.string().max(100) }).optional(), preferences: z.record(z.unknown()).optional(), createdAt: z.date(), updatedAt: z.date(), createdBy: UserIdSchema.optional(), updatedBy: UserIdSchema.optional() }); export const UserSessionSchema = z.object({ id: z.string().uuid(), userId: UserIdSchema, token: z.string(), refreshToken: z.string().optional(), deviceInfo: z.object({ userAgent: z.string(), ipAddress: z.string().ip(), deviceType: z.enum(['desktop', 'mobile', 'tablet']), os: z.string(), browser: z.string() }).optional(), isActive: z.boolean(), expiresAt: z.date(), lastActivityAt: z.date(), createdAt: z.date() }); export const UserSchema = z.object({ id: UserIdSchema, email: EmailValueObjectSchema, password: PasswordValueObjectSchema, role: UserRoleSchema, status: UserStatusSchema, profile: UserProfileSchema, sessions: z.array(UserSessionSchema).optional(), permissions: z.array(z.string()).optional(), lastLoginAt: z.date().optional(), loginAttempts: z.number().min(0).max(10).optional(), lockedUntil: z.date().optional(), emailVerifiedAt: z.date().optional(), twoFactorEnabled: z.boolean().optional(), twoFactorSecret: z.string().optional(), createdAt: z.date(), updatedAt: z.date(), createdBy: UserIdSchema.optional(), updatedBy: UserIdSchema.optional(), deletedAt: z.date().optional() }); // ============================================================================ // REQUEST/INPUT SCHEMAS // ============================================================================ export const CreateUserRequestSchema = z.object({ email: EmailSchema, password: PasswordSchema, firstName: z.string().min(1).max(50), lastName: z.string().min(1).max(50), role: UserRoleSchema.optional(), displayName: z.string().max(100).optional(), phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/).optional(), address: z.object({ street: z.string().max(200), city: z.string().max(100), state: z.string().max(100), postalCode: z.string().max(20), country: z.string().max(100) }).optional(), preferences: z.record(z.unknown()).optional() }); export const UpdateUserRequestSchema = z.object({ firstName: z.string().min(1).max(50).optional(), lastName: z.string().min(1).max(50).optional(), displayName: z.string().max(100).optional(), phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/).optional(), address: z.object({ street: z.string().max(200), city: z.string().max(100), state: z.string().max(100), postalCode: z.string().max(20), country: z.string().max(100) }).optional(), preferences: z.record(z.unknown()).optional(), role: UserRoleSchema.optional(), status: UserStatusSchema.optional() }); export const ChangePasswordRequestSchema = z.object({ currentPassword: z.string(), newPassword: PasswordSchema, confirmPassword: z.string() }).refine((data) => data.newPassword === data.confirmPassword, { message: "Passwords don't match", path: ["confirmPassword"] }); export const LoginRequestSchema = z.object({ email: EmailSchema, password: z.string(), rememberMe: z.boolean().optional(), deviceInfo: z.object({ userAgent: z.string(), ipAddress: z.string().ip(), deviceType: z.enum(['desktop', 'mobile', 'tablet']).optional(), os: z.string().optional(), browser: z.string().optional() }).optional() }); export const ResetPasswordRequestSchema = z.object({ email: EmailSchema }); export const VerifyEmailRequestSchema = z.object({ token: z.string() }); export const EnableTwoFactorRequestSchema = z.object({ secret: z.string(), code: z.string().length(6) }); export const DisableTwoFactorRequestSchema = z.object({ password: z.string(), code: z.string().length(6) }); // ============================================================================ // RESPONSE SCHEMAS // ============================================================================ export const UserResponseSchema = z.object({ id: UserIdSchema, email: z.string().email(), role: UserRoleSchema, status: UserStatusSchema, profile: z.object({ firstName: z.string(), lastName: z.string(), displayName: z.string().optional(), avatar: z.string().url().optional(), phoneNumber: z.string().optional() }), lastLoginAt: z.date().optional(), emailVerifiedAt: z.date().optional(), twoFactorEnabled: z.boolean(), createdAt: z.date(), updatedAt: z.date() }); export const UserListResponseSchema = z.object({ users: z.array(UserResponseSchema), pagination: z.object({ page: z.number().positive(), limit: z.number().positive(), total: z.number().nonnegative(), totalPages: z.number().nonnegative() }) }); export const LoginResponseSchema = z.object({ user: UserResponseSchema, session: z.object({ token: z.string(), refreshToken: z.string(), expiresAt: z.date() }) }); export const PasswordResetResponseSchema = z.object({ message: z.string(), resetToken: z.string().optional() }); // ============================================================================ // FILTER/SEARCH SCHEMAS // ============================================================================ export const UserSearchFiltersSchema = z.object({ query: z.string().optional(), role: UserRoleSchema.optional(), status: UserStatusSchema.optional(), emailVerified: z.boolean().optional(), twoFactorEnabled: z.boolean().optional(), createdAfter: z.date().optional(), createdBefore: z.date().optional(), lastLoginAfter: z.date().optional(), lastLoginBefore: z.date().optional() }); export const UserSortOptionsSchema = z.enum([ 'createdAt', 'updatedAt', 'lastLoginAt', 'firstName', 'lastName', 'email', 'role', 'status' ]); // ============================================================================ // VALIDATION UTILITIES // ============================================================================ export const UserValidationUtils = { isStrongPassword: (password) => { return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(password); }, isValidEmail: (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }, isPhoneNumber: (phone) => { return /^\+?[1-9]\d{1,14}$/.test(phone); }, validatePasswordStrength: (password) => { const errors = []; if (password.length < 8) { errors.push('Password must be at least 8 characters long'); } if (!/[a-z]/.test(password)) { errors.push('Password must contain at least one lowercase letter'); } if (!/[A-Z]/.test(password)) { errors.push('Password must contain at least one uppercase letter'); } if (!/\d/.test(password)) { errors.push('Password must contain at least one number'); } if (!/[@$!%*?&]/.test(password)) { errors.push('Password must contain at least one special character (@$!%*?&)'); } return { isValid: errors.length === 0, errors }; } }; // ============================================================================ // SCHEMA EXPORTS // ============================================================================ export const UserSchemas = { // Base schemas UserId: UserIdSchema, Email: EmailSchema, Password: PasswordSchema, // Enum schemas UserRole: UserRoleSchema, UserStatus: UserStatusSchema, // Value object schemas UserIdValueObject: UserIdValueObjectSchema, EmailValueObject: EmailValueObjectSchema, PasswordValueObject: PasswordValueObjectSchema, // Entity schemas User: UserSchema, UserProfile: UserProfileSchema, UserSession: UserSessionSchema, // Request schemas CreateUserRequest: CreateUserRequestSchema, UpdateUserRequest: UpdateUserRequestSchema, ChangePasswordRequest: ChangePasswordRequestSchema, LoginRequest: LoginRequestSchema, ResetPasswordRequest: ResetPasswordRequestSchema, VerifyEmailRequest: VerifyEmailRequestSchema, EnableTwoFactorRequest: EnableTwoFactorRequestSchema, DisableTwoFactorRequest: DisableTwoFactorRequestSchema, // Response schemas UserResponse: UserResponseSchema, UserListResponse: UserListResponseSchema, LoginResponse: LoginResponseSchema, PasswordResetResponse: PasswordResetResponseSchema, // Filter schemas UserSearchFilters: UserSearchFiltersSchema, UserSortOptions: UserSortOptionsSchema, // Utilities ValidationUtils: UserValidationUtils }; //# sourceMappingURL=user-schemas.js.map