UNPKG

emr-types

Version:

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

383 lines 16.1 kB
import { z } from 'zod'; import { MedicalRecordStatus } from '../domains/medical-record'; // ============================================================================ // BASE SCHEMAS // ============================================================================ export const MedicalRecordIdSchema = z.string().uuid().brand(); // ============================================================================ // ENUM SCHEMAS // ============================================================================ export const MedicalRecordStatusSchema = z.nativeEnum(MedicalRecordStatus); // ============================================================================ // VALUE OBJECT SCHEMAS // ============================================================================ export const MedicalRecordIdValueObjectSchema = z.object({ value: MedicalRecordIdSchema, format: z.enum(['uuid', 'auto-increment', 'custom']), createdAt: z.date(), metadata: z.record(z.unknown()).optional() }); // ============================================================================ // ENTITY SCHEMAS // ============================================================================ export const DiagnosisSchema = z.object({ id: z.string().uuid(), condition: z.string().min(1).max(100), icd10Code: z.string().max(20).optional(), icd11Code: z.string().max(20).optional(), severity: z.enum(['mild', 'moderate', 'severe']), diagnosisDate: z.date(), isPrimary: z.boolean(), isActive: z.boolean(), notes: z.string().max(500).optional(), diagnosedBy: z.string().uuid().optional(), createdAt: z.date(), updatedAt: z.date() }); export const PrescriptionSchema = z.object({ id: z.string().uuid(), medication: z.string().min(1).max(100), ndcCode: z.string().max(20).optional(), rxNormCode: z.string().max(20).optional(), dosage: z.string().max(50), frequency: z.string().max(50), route: z.enum(['oral', 'intravenous', 'intramuscular', 'subcutaneous', 'topical', 'inhalation']), quantity: z.number().positive(), refills: z.number().nonnegative(), startDate: z.date(), endDate: z.date().optional(), isActive: z.boolean(), instructions: z.string().max(500).optional(), prescribedBy: z.string().uuid(), prescribedAt: z.date(), createdAt: z.date(), updatedAt: z.date() }); export const ProcedureSchema = z.object({ id: z.string().uuid(), name: z.string().min(1).max(100), cptCode: z.string().max(20).optional(), hcpcsCode: z.string().max(20).optional(), description: z.string().max(500).optional(), procedureDate: z.date(), performedBy: z.string().uuid().optional(), facility: z.string().max(100).optional(), anesthesiaType: z.enum(['none', 'local', 'regional', 'general']).optional(), complications: z.string().max(500).optional(), notes: z.string().max(500).optional(), createdAt: z.date(), updatedAt: z.date() }); export const MedicalRecordSchema = z.object({ id: MedicalRecordIdSchema, patientId: z.string().uuid(), providerId: z.string().uuid(), status: MedicalRecordStatusSchema, visitDate: z.date(), chiefComplaint: z.string().min(1).max(500), historyOfPresentIllness: z.string().max(1000).optional(), physicalExamination: z.object({ vitalSigns: z.object({ temperature: z.number().optional(), bloodPressure: z.string().max(20).optional(), heartRate: z.number().positive().optional(), respiratoryRate: z.number().positive().optional(), oxygenSaturation: z.number().min(0).max(100).optional(), height: z.number().positive().optional(), weight: z.number().positive().optional(), bmi: z.number().positive().optional() }).optional(), generalAppearance: z.string().max(200).optional(), cardiovascular: z.string().max(200).optional(), respiratory: z.string().max(200).optional(), gastrointestinal: z.string().max(200).optional(), musculoskeletal: z.string().max(200).optional(), neurological: z.string().max(200).optional(), skin: z.string().max(200).optional(), notes: z.string().max(1000).optional() }).optional(), assessment: z.string().max(1000).optional(), plan: z.string().max(1000).optional(), diagnoses: z.array(DiagnosisSchema).optional(), prescriptions: z.array(PrescriptionSchema).optional(), procedures: z.array(ProcedureSchema).optional(), labResults: z.array(z.object({ id: z.string().uuid(), testName: z.string().min(1).max(100), loincCode: z.string().max(20).optional(), value: z.string().max(100), unit: z.string().max(20).optional(), referenceRange: z.string().max(100).optional(), isAbnormal: z.boolean(), notes: z.string().max(500).optional(), reportedAt: z.date() })).optional(), imagingResults: z.array(z.object({ id: z.string().uuid(), studyName: z.string().min(1).max(100), modality: z.enum(['x-ray', 'ct', 'mri', 'ultrasound', 'mammography', 'other']), bodyPart: z.string().max(100), report: z.string().max(1000), images: z.array(z.string().url()), radiologist: z.string().max(100).optional(), reportedAt: z.date() })).optional(), attachments: z.array(z.object({ id: z.string().uuid(), name: z.string().min(1).max(100), type: z.enum(['document', 'image', 'video', 'audio']), url: z.string().url(), size: z.number().positive(), mimeType: z.string(), uploadedBy: z.string().uuid(), uploadedAt: z.date() })).optional(), createdAt: z.date(), updatedAt: z.date(), createdBy: z.string().uuid().optional(), updatedBy: z.string().uuid().optional() }); // ============================================================================ // REQUEST/INPUT SCHEMAS // ============================================================================ export const CreateMedicalRecordRequestSchema = z.object({ patientId: z.string().uuid(), providerId: z.string().uuid(), visitDate: z.date(), chiefComplaint: z.string().min(1).max(500), historyOfPresentIllness: z.string().max(1000).optional(), physicalExamination: z.object({ vitalSigns: z.object({ temperature: z.number().optional(), bloodPressure: z.string().max(20).optional(), heartRate: z.number().positive().optional(), respiratoryRate: z.number().positive().optional(), oxygenSaturation: z.number().min(0).max(100).optional(), height: z.number().positive().optional(), weight: z.number().positive().optional() }).optional(), generalAppearance: z.string().max(200).optional(), cardiovascular: z.string().max(200).optional(), respiratory: z.string().max(200).optional(), gastrointestinal: z.string().max(200).optional(), musculoskeletal: z.string().max(200).optional(), neurological: z.string().max(200).optional(), skin: z.string().max(200).optional(), notes: z.string().max(1000).optional() }).optional(), assessment: z.string().max(1000).optional(), plan: z.string().max(1000).optional() }); export const UpdateMedicalRecordRequestSchema = z.object({ status: MedicalRecordStatusSchema.optional(), chiefComplaint: z.string().min(1).max(500).optional(), historyOfPresentIllness: z.string().max(1000).optional(), physicalExamination: z.object({ vitalSigns: z.object({ temperature: z.number().optional(), bloodPressure: z.string().max(20).optional(), heartRate: z.number().positive().optional(), respiratoryRate: z.number().positive().optional(), oxygenSaturation: z.number().min(0).max(100).optional(), height: z.number().positive().optional(), weight: z.number().positive().optional() }).optional(), generalAppearance: z.string().max(200).optional(), cardiovascular: z.string().max(200).optional(), respiratory: z.string().max(200).optional(), gastrointestinal: z.string().max(200).optional(), musculoskeletal: z.string().max(200).optional(), neurological: z.string().max(200).optional(), skin: z.string().max(200).optional(), notes: z.string().max(1000).optional() }).optional(), assessment: z.string().max(1000).optional(), plan: z.string().max(1000).optional() }); export const AddDiagnosisRequestSchema = z.object({ condition: z.string().min(1).max(100), icd10Code: z.string().max(20).optional(), icd11Code: z.string().max(20).optional(), severity: z.enum(['mild', 'moderate', 'severe']), diagnosisDate: z.date(), isPrimary: z.boolean().optional(), notes: z.string().max(500).optional() }); export const AddPrescriptionRequestSchema = z.object({ medication: z.string().min(1).max(100), ndcCode: z.string().max(20).optional(), rxNormCode: z.string().max(20).optional(), dosage: z.string().max(50), frequency: z.string().max(50), route: z.enum(['oral', 'intravenous', 'intramuscular', 'subcutaneous', 'topical', 'inhalation']), quantity: z.number().positive(), refills: z.number().nonnegative(), startDate: z.date(), endDate: z.date().optional(), instructions: z.string().max(500).optional() }); export const AddProcedureRequestSchema = z.object({ name: z.string().min(1).max(100), cptCode: z.string().max(20).optional(), hcpcsCode: z.string().max(20).optional(), description: z.string().max(500).optional(), procedureDate: z.date(), facility: z.string().max(100).optional(), anesthesiaType: z.enum(['none', 'local', 'regional', 'general']).optional(), complications: z.string().max(500).optional(), notes: z.string().max(500).optional() }); // ============================================================================ // RESPONSE SCHEMAS // ============================================================================ export const MedicalRecordResponseSchema = z.object({ id: MedicalRecordIdSchema, patientId: z.string().uuid(), providerId: z.string().uuid(), status: MedicalRecordStatusSchema, visitDate: z.date(), chiefComplaint: z.string(), historyOfPresentIllness: z.string().optional(), assessment: z.string().optional(), plan: z.string().optional(), diagnoses: z.array(z.object({ condition: z.string(), icd10Code: z.string().optional(), severity: z.enum(['mild', 'moderate', 'severe']), diagnosisDate: z.date(), isActive: z.boolean() })).optional(), prescriptions: z.array(z.object({ medication: z.string(), dosage: z.string(), frequency: z.string(), isActive: z.boolean() })).optional(), procedures: z.array(z.object({ name: z.string(), cptCode: z.string().optional(), procedureDate: z.date() })).optional(), createdAt: z.date(), updatedAt: z.date() }); export const MedicalRecordListResponseSchema = z.object({ medicalRecords: z.array(MedicalRecordResponseSchema), pagination: z.object({ page: z.number().positive(), limit: z.number().positive(), total: z.number().nonnegative(), totalPages: z.number().nonnegative() }) }); // ============================================================================ // FILTER/SEARCH SCHEMAS // ============================================================================ export const MedicalRecordSearchFiltersSchema = z.object({ query: z.string().optional(), patientId: z.string().uuid().optional(), providerId: z.string().uuid().optional(), status: MedicalRecordStatusSchema.optional(), visitDateAfter: z.date().optional(), visitDateBefore: z.date().optional(), hasDiagnoses: z.boolean().optional(), hasPrescriptions: z.boolean().optional(), hasProcedures: z.boolean().optional(), hasLabResults: z.boolean().optional(), hasImagingResults: z.boolean().optional(), createdAfter: z.date().optional(), createdBefore: z.date().optional() }); export const MedicalRecordSortOptionsSchema = z.enum([ 'visitDate', 'createdAt', 'updatedAt', 'status', 'patientName', 'providerName' ]); // ============================================================================ // VALIDATION UTILITIES // ============================================================================ export const MedicalRecordValidationUtils = { validateICD10Code: (code) => { // Basic ICD-10 validation pattern return /^[A-Z]\d{2}(\.\d{1,2})?$/.test(code); }, validateICD11Code: (code) => { // Basic ICD-11 validation pattern return /^[A-Z]{2}\d{2}\.\d{1,2}$/.test(code); }, validateCPTCode: (code) => { // Basic CPT code validation pattern return /^\d{5}$/.test(code); }, validateNDCCode: (code) => { // Basic NDC code validation pattern return /^\d{4}-\d{4}-\d{2}$/.test(code); }, validateRxNormCode: (code) => { // Basic RxNorm code validation pattern return /^\d+$/.test(code); }, validateLOINCCode: (code) => { // Basic LOINC code validation pattern return /^\d{1,6}-\d$/.test(code); }, validateMedicalRecordData: (data) => { const errors = []; if (data.visitDate && new Date(data.visitDate) > new Date()) { errors.push('Visit date cannot be in the future'); } if (data.physicalExamination?.vitalSigns) { const vitals = data.physicalExamination.vitalSigns; if (vitals.temperature && (vitals.temperature < 30 || vitals.temperature > 45)) { errors.push('Temperature must be between 30 and 45 degrees Celsius'); } if (vitals.heartRate && (vitals.heartRate < 30 || vitals.heartRate > 250)) { errors.push('Heart rate must be between 30 and 250 bpm'); } if (vitals.respiratoryRate && (vitals.respiratoryRate < 5 || vitals.respiratoryRate > 60)) { errors.push('Respiratory rate must be between 5 and 60 breaths per minute'); } if (vitals.oxygenSaturation && (vitals.oxygenSaturation < 70 || vitals.oxygenSaturation > 100)) { errors.push('Oxygen saturation must be between 70 and 100 percent'); } } return { isValid: errors.length === 0, errors }; } }; // ============================================================================ // SCHEMA EXPORTS // ============================================================================ export const MedicalRecordSchemas = { // Base schemas MedicalRecordId: MedicalRecordIdSchema, // Enum schemas MedicalRecordStatus: MedicalRecordStatusSchema, // Value object schemas MedicalRecordIdValueObject: MedicalRecordIdValueObjectSchema, // Entity schemas MedicalRecord: MedicalRecordSchema, Diagnosis: DiagnosisSchema, Prescription: PrescriptionSchema, Procedure: ProcedureSchema, // Request schemas CreateMedicalRecordRequest: CreateMedicalRecordRequestSchema, UpdateMedicalRecordRequest: UpdateMedicalRecordRequestSchema, AddDiagnosisRequest: AddDiagnosisRequestSchema, AddPrescriptionRequest: AddPrescriptionRequestSchema, AddProcedureRequest: AddProcedureRequestSchema, // Response schemas MedicalRecordResponse: MedicalRecordResponseSchema, MedicalRecordListResponse: MedicalRecordListResponseSchema, // Filter schemas MedicalRecordSearchFilters: MedicalRecordSearchFiltersSchema, MedicalRecordSortOptions: MedicalRecordSortOptionsSchema, // Utilities ValidationUtils: MedicalRecordValidationUtils }; //# sourceMappingURL=medical-record-schemas.js.map