emr-types
Version:
Comprehensive TypeScript Types Library for Electronic Medical Record (EMR) Applications - Domain-Driven Design with Zod Validation
297 lines • 13.5 kB
JavaScript
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