UNPKG

emr-types

Version:

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

297 lines 13.5 kB
import { z } from 'zod'; import { UserSchemas, TenantSchemas, PatientSchemas, AppointmentSchemas, MedicalRecordSchemas, SharedSchemas } from '../schemas'; // ============================================================================ // CORE VALIDATION FUNCTIONS // ============================================================================ /** * Validate data against a Zod schema with detailed error information */ export function validateSchema(schema, data) { try { const result = schema.parse(data); return { success: true, data: result }; } catch (error) { if (error instanceof z.ZodError) { return { success: false, errors: error.errors.map(err => err.message), details: error }; } return { success: false, errors: [error instanceof Error ? error.message : 'Validation failed'] }; } } /** * Safe parse that doesn't throw exceptions */ export function safeParse(schema, data) { const result = schema.safeParse(data); if (result.success) { return { success: true, data: result.data }; } else { return { success: false, errors: result.error.errors.map(err => err.message), details: result.error }; } } /** * Validate data and return simple boolean result */ export function isValid(schema, data) { return schema.safeParse(data).success; } /** * Transform data using schema (useful for data cleaning) */ export function transform(schema, data) { return schema.parse(data); } // ============================================================================ // DOMAIN-SPECIFIC VALIDATION FUNCTIONS // ============================================================================ // User Domain Validation export const UserValidation = { validateCreateUser: (data) => validateSchema(UserSchemas.CreateUserRequest, data), validateUpdateUser: (data) => validateSchema(UserSchemas.UpdateUserRequest, data), validateLogin: (data) => validateSchema(UserSchemas.LoginRequest, data), validateChangePassword: (data) => validateSchema(UserSchemas.ChangePasswordRequest, data), validateUser: (data) => validateSchema(UserSchemas.User, data), validateUserResponse: (data) => validateSchema(UserSchemas.UserResponse, data), validateUserList: (data) => validateSchema(UserSchemas.UserListResponse, data), // Utility functions isValidEmail: (email) => UserSchemas.ValidationUtils.isValidEmail(email), isStrongPassword: (password) => UserSchemas.ValidationUtils.isStrongPassword(password), validatePasswordStrength: (password) => UserSchemas.ValidationUtils.validatePasswordStrength(password) }; // Tenant Domain Validation export const TenantValidation = { validateCreateTenant: (data) => validateSchema(TenantSchemas.CreateTenantRequest, data), validateUpdateTenant: (data) => validateSchema(TenantSchemas.UpdateTenantRequest, data), validateActivateTenant: (data) => validateSchema(TenantSchemas.ActivateTenantRequest, data), validateTenant: (data) => validateSchema(TenantSchemas.Tenant, data), validateTenantResponse: (data) => validateSchema(TenantSchemas.TenantResponse, data), validateTenantList: (data) => validateSchema(TenantSchemas.TenantListResponse, data), // Utility functions isValidDomainName: (domain) => TenantSchemas.ValidationUtils.isValidDomainName(domain), isValidSlug: (slug) => TenantSchemas.ValidationUtils.isValidSlug(slug), isOverUsageLimit: (usage, limits) => TenantSchemas.ValidationUtils.isOverUsageLimit(usage, limits), calculateUsagePercentage: (current, limit) => TenantSchemas.ValidationUtils.calculateUsagePercentage(current, limit) }; // Patient Domain Validation export const PatientValidation = { validateCreatePatient: (data) => validateSchema(PatientSchemas.CreatePatientRequest, data), validateUpdatePatient: (data) => validateSchema(PatientSchemas.UpdatePatientRequest, data), validateAddMedicalHistory: (data) => validateSchema(PatientSchemas.AddMedicalHistoryRequest, data), validatePatient: (data) => validateSchema(PatientSchemas.Patient, data), validatePatientResponse: (data) => validateSchema(PatientSchemas.PatientResponse, data), validatePatientList: (data) => validateSchema(PatientSchemas.PatientListResponse, data), // Utility functions isValidPhoneNumber: (phone) => PatientSchemas.ValidationUtils.isValidPhoneNumber(phone), calculateAge: (dateOfBirth) => PatientSchemas.ValidationUtils.calculateAge(dateOfBirth), calculateBMI: (height, weight) => PatientSchemas.ValidationUtils.calculateBMI(height, weight), getBMICategory: (bmi) => PatientSchemas.ValidationUtils.getBMICategory(bmi) }; // Appointment Domain Validation export const AppointmentValidation = { validateCreateAppointment: (data) => validateSchema(AppointmentSchemas.CreateAppointmentRequest, data), validateUpdateAppointment: (data) => validateSchema(AppointmentSchemas.UpdateAppointmentRequest, data), validateCancelAppointment: (data) => validateSchema(AppointmentSchemas.CancelAppointmentRequest, data), validateRescheduleAppointment: (data) => validateSchema(AppointmentSchemas.RescheduleAppointmentRequest, data), validateAppointment: (data) => validateSchema(AppointmentSchemas.Appointment, data), validateAppointmentResponse: (data) => validateSchema(AppointmentSchemas.AppointmentResponse, data), validateAppointmentList: (data) => validateSchema(AppointmentSchemas.AppointmentListResponse, data), // Utility functions isValidTimeSlot: (startTime, endTime) => AppointmentSchemas.ValidationUtils.isValidTimeSlot(startTime, endTime), calculateDuration: (startTime, endTime) => AppointmentSchemas.ValidationUtils.calculateDuration(startTime, endTime), isBusinessHours: (date) => AppointmentSchemas.ValidationUtils.isBusinessHours(date), isAvailableTimeSlot: (startTime, endTime, existingAppointments) => AppointmentSchemas.ValidationUtils.isAvailableTimeSlot(startTime, endTime, existingAppointments) }; // Medical Record Domain Validation export const MedicalRecordValidation = { validateCreateMedicalRecord: (data) => validateSchema(MedicalRecordSchemas.CreateMedicalRecordRequest, data), validateUpdateMedicalRecord: (data) => validateSchema(MedicalRecordSchemas.UpdateMedicalRecordRequest, data), validateAddDiagnosis: (data) => validateSchema(MedicalRecordSchemas.AddDiagnosisRequest, data), validateAddPrescription: (data) => validateSchema(MedicalRecordSchemas.AddPrescriptionRequest, data), validateAddProcedure: (data) => validateSchema(MedicalRecordSchemas.AddProcedureRequest, data), validateMedicalRecord: (data) => validateSchema(MedicalRecordSchemas.MedicalRecord, data), validateMedicalRecordResponse: (data) => validateSchema(MedicalRecordSchemas.MedicalRecordResponse, data), validateMedicalRecordList: (data) => validateSchema(MedicalRecordSchemas.MedicalRecordListResponse, data), // Utility functions validateICD10Code: (code) => MedicalRecordSchemas.ValidationUtils.validateICD10Code(code), validateICD11Code: (code) => MedicalRecordSchemas.ValidationUtils.validateICD11Code(code), validateCPTCode: (code) => MedicalRecordSchemas.ValidationUtils.validateCPTCode(code), validateNDCCode: (code) => MedicalRecordSchemas.ValidationUtils.validateNDCCode(code), validateRxNormCode: (code) => MedicalRecordSchemas.ValidationUtils.validateRxNormCode(code), validateLOINCCode: (code) => MedicalRecordSchemas.ValidationUtils.validateLOINCCode(code) }; // Shared Validation export const SharedValidation = { validatePagination: (data) => validateSchema(SharedSchemas.Pagination, data), validateSort: (data) => validateSchema(SharedSchemas.Sort, data), validateFilter: (data) => validateSchema(SharedSchemas.Filter, data), validateApiResponse: (data) => validateSchema(SharedSchemas.ApiResponse, data), validatePaginatedResponse: (data) => validateSchema(SharedSchemas.PaginatedResponse, data), validateErrorResponse: (data) => validateSchema(SharedSchemas.ErrorResponse, data), // Utility functions isValidUUID: (uuid) => SharedSchemas.ValidationUtils.isValidUUID(uuid), isValidEmail: (email) => SharedSchemas.ValidationUtils.isValidEmail(email), isValidPhoneNumber: (phone) => SharedSchemas.ValidationUtils.isValidPhoneNumber(phone), isValidCurrency: (currency) => SharedSchemas.ValidationUtils.isValidCurrency(currency), isValidMoney: (amount, currency) => SharedSchemas.ValidationUtils.isValidMoney(amount, currency) }; // ============================================================================ // BATCH VALIDATION FUNCTIONS // ============================================================================ /** * Validate multiple items against the same schema */ export function validateBatch(schema, items) { const results = items.map(item => validateSchema(schema, item)); const allValid = results.every(result => result.success); return { results, allValid }; } /** * Validate an object with multiple schemas for different properties */ export function validateObject(schemas, data) { const errors = []; const validatedData = {}; for (const [key, schema] of Object.entries(schemas)) { if (key in data) { const result = validateSchema(schema, data[key]); if (result.success) { validatedData[key] = result.data; } else { errors.push(...(result.errors?.map(err => `${key}: ${err}`) || [])); } } } if (errors.length === 0) { return { success: true, data: validatedData }; } else { return { success: false, errors }; } } // ============================================================================ // ERROR HANDLING UTILITIES // ============================================================================ /** * Format validation errors for user display */ export function formatValidationErrors(errors) { return errors.join('; '); } /** * Create a user-friendly error message from validation result */ export function createErrorMessage(result) { if (result.success) { return 'Validation successful'; } if (result.errors && result.errors.length > 0) { return `Validation failed: ${formatValidationErrors(result.errors)}`; } return 'Validation failed'; } /** * Extract field-specific errors from validation result */ export function extractFieldErrors(result) { const fieldErrors = {}; if (!result.success && result.details) { result.details.errors.forEach(error => { const field = error.path.join('.'); if (!fieldErrors[field]) { fieldErrors[field] = []; } fieldErrors[field].push(error.message); }); } return fieldErrors; } // ============================================================================ // VALIDATION HELPERS // ============================================================================ /** * Create a custom validator with custom error messages */ export function createCustomValidator(schema, customErrorMessages = {}) { return (data) => { try { const result = schema.parse(data); return { success: true, data: result }; } catch (error) { if (error instanceof z.ZodError) { const errors = error.errors.map(err => { const field = err.path.join('.'); return customErrorMessages[field] || err.message; }); return { success: false, errors }; } return { success: false, errors: ['Validation failed'] }; } }; } /** * Create a partial validator that only validates provided fields */ export function createPartialValidator(schema, _requiredFields) { // For now, return a simple validator that accepts partial data return (data) => { try { // Create a partial schema using safeParse const result = schema.safeParse(data); if (result.success) { return { success: true, data: result.data }; } else { return { success: false, errors: result.error.errors.map(err => err.message), details: result.error }; } } catch (error) { return { success: false, errors: ['Validation failed'] }; } }; } // ============================================================================ // EXPORTS // ============================================================================ export const ValidationUtils = { // Core functions validateSchema, safeParse, isValid, transform, // Batch functions validateBatch, validateObject, // Error handling formatValidationErrors, createErrorMessage, extractFieldErrors, // Helpers createCustomValidator, createPartialValidator, // Domain-specific validation User: UserValidation, Tenant: TenantValidation, Patient: PatientValidation, Appointment: AppointmentValidation, MedicalRecord: MedicalRecordValidation, Shared: SharedValidation }; //# sourceMappingURL=validation.js.map