UNPKG

emr-types

Version:

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

295 lines 10.8 kB
import { z } from 'zod'; import { Status, Priority } from '../domains/shared'; // ============================================================================ // BASE SCHEMAS // ============================================================================ export const IdSchema = z.string().uuid().brand(); export const TimestampSchema = z.date(); export const MoneySchema = z.object({ amount: z.number(), currency: z.string().length(3), precision: z.number().min(0).max(4).default(2) }); // ============================================================================ // ENUM SCHEMAS // ============================================================================ export const StatusSchema = z.nativeEnum(Status); export const PrioritySchema = z.nativeEnum(Priority); // ============================================================================ // VALUE OBJECT SCHEMAS // ============================================================================ export const IdValueObjectSchema = z.object({ value: IdSchema, format: z.enum(['uuid', 'auto-increment', 'custom']), createdAt: TimestampSchema, metadata: z.record(z.unknown()).optional() }); export const TimestampValueObjectSchema = z.object({ value: TimestampSchema, timezone: z.string().optional(), format: z.string().optional(), createdAt: TimestampSchema }); export const MoneyValueObjectSchema = z.object({ value: MoneySchema, exchangeRate: z.number().positive().optional(), baseCurrency: z.string().length(3).optional(), createdAt: TimestampSchema }); // ============================================================================ // COMMON SCHEMAS // ============================================================================ export const PaginationSchema = z.object({ page: z.number().positive(), limit: z.number().positive().max(1000), total: z.number().nonnegative(), totalPages: z.number().nonnegative() }); export const SortSchema = z.object({ field: z.string(), direction: z.enum(['asc', 'desc']) }); export const FilterSchema = z.object({ field: z.string(), operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'like', 'ilike']), value: z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number()]))]) }); export const SearchSchema = z.object({ query: z.string(), fields: z.array(z.string()).optional(), fuzzy: z.boolean().optional() }); export const ApiResponseSchema = z.object({ success: z.boolean(), data: z.unknown().optional(), message: z.string().optional(), errors: z.array(z.string()).optional(), meta: z.object({ timestamp: TimestampSchema, requestId: z.string().uuid().optional(), version: z.string().optional() }).optional() }); export const PaginatedResponseSchema = z.object({ data: z.array(z.unknown()), pagination: PaginationSchema, meta: z.object({ timestamp: TimestampSchema, requestId: z.string().uuid().optional(), version: z.string().optional() }).optional() }); export const ErrorResponseSchema = z.object({ success: z.literal(false), error: z.object({ code: z.string(), message: z.string(), details: z.unknown().optional(), stack: z.string().optional() }), meta: z.object({ timestamp: TimestampSchema, requestId: z.string().uuid().optional(), version: z.string().optional() }).optional() }); // ============================================================================ // VALIDATION SCHEMAS // ============================================================================ export const ValidationErrorSchema = z.object({ field: z.string(), message: z.string(), code: z.string().optional(), value: z.unknown().optional() }); export const ValidationResultSchema = z.object({ isValid: z.boolean(), errors: z.array(ValidationErrorSchema), warnings: z.array(ValidationErrorSchema).optional() }); // ============================================================================ // AUDIT SCHEMAS // ============================================================================ export const AuditMetadataSchema = z.object({ createdAt: TimestampSchema, createdBy: IdSchema.optional(), updatedAt: TimestampSchema.optional(), updatedBy: IdSchema.optional(), deletedAt: TimestampSchema.optional(), deletedBy: IdSchema.optional(), version: z.number().positive().optional() }); export const AuditLogSchema = z.object({ id: IdSchema, entityType: z.string(), entityId: IdSchema, action: z.enum(['create', 'update', 'delete', 'restore']), changes: z.object({ before: z.record(z.unknown()).optional(), after: z.record(z.unknown()).optional() }).optional(), userId: IdSchema.optional(), userAgent: z.string().optional(), ipAddress: z.string().ip().optional(), timestamp: TimestampSchema, metadata: z.record(z.unknown()).optional() }); // ============================================================================ // FILE SCHEMAS // ============================================================================ export const FileSchema = z.object({ id: IdSchema, name: z.string().min(1).max(255), originalName: z.string().min(1).max(255), mimeType: z.string(), size: z.number().positive(), url: z.string().url(), path: z.string().optional(), hash: z.string().optional(), metadata: z.record(z.unknown()).optional(), createdAt: TimestampSchema, createdBy: IdSchema.optional() }); export const FileUploadSchema = z.object({ file: z.instanceof(File), category: z.string().optional(), tags: z.array(z.string()).optional(), metadata: z.record(z.unknown()).optional() }); // ============================================================================ // NOTIFICATION SCHEMAS // ============================================================================ export const NotificationSchema = z.object({ id: IdSchema, type: z.enum(['info', 'success', 'warning', 'error']), title: z.string().min(1).max(100), message: z.string().min(1).max(500), recipientId: IdSchema, recipientType: z.enum(['user', 'patient', 'provider']), channel: z.enum(['email', 'sms', 'push', 'in-app']), status: z.enum(['pending', 'sent', 'delivered', 'failed']), scheduledAt: TimestampSchema.optional(), sentAt: TimestampSchema.optional(), deliveredAt: TimestampSchema.optional(), metadata: z.record(z.unknown()).optional(), createdAt: TimestampSchema }); // ============================================================================ // SETTINGS SCHEMAS // ============================================================================ export const SettingsSchema = z.object({ id: IdSchema, key: z.string().min(1).max(100), value: z.unknown(), type: z.enum(['string', 'number', 'boolean', 'object', 'array']), category: z.string().max(50).optional(), description: z.string().max(500).optional(), isPublic: z.boolean().default(false), isRequired: z.boolean().default(false), validation: z.record(z.unknown()).optional(), createdAt: TimestampSchema, updatedAt: TimestampSchema.optional() }); // ============================================================================ // VALIDATION UTILITIES // ============================================================================ export const SharedValidationUtils = { isValidUUID: (uuid) => { return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid); }, isValidEmail: (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }, isValidPhoneNumber: (phone) => { return /^\+?[1-9]\d{1,14}$/.test(phone); }, isValidCurrency: (currency) => { return /^[A-Z]{3}$/.test(currency); }, isValidMoney: (amount, currency) => { return amount >= 0 && SharedValidationUtils.isValidCurrency(currency); }, validatePagination: (page, limit) => { const errors = []; if (page < 1) { errors.push('Page must be greater than 0'); } if (limit < 1 || limit > 1000) { errors.push('Limit must be between 1 and 1000'); } return { isValid: errors.length === 0, errors }; }, validateSort: (field, direction) => { const errors = []; if (!field || field.trim().length === 0) { errors.push('Sort field is required'); } if (!['asc', 'desc'].includes(direction)) { errors.push('Sort direction must be "asc" or "desc"'); } return { isValid: errors.length === 0, errors }; }, validateFilter: (field, operator, value) => { const errors = []; if (!field || field.trim().length === 0) { errors.push('Filter field is required'); } const validOperators = ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'like', 'ilike']; if (!validOperators.includes(operator)) { errors.push(`Invalid operator. Must be one of: ${validOperators.join(', ')}`); } if (value === undefined || value === null) { errors.push('Filter value is required'); } return { isValid: errors.length === 0, errors }; } }; // ============================================================================ // SCHEMA EXPORTS // ============================================================================ export const SharedSchemas = { // Base schemas Id: IdSchema, Timestamp: TimestampSchema, Money: MoneySchema, // Enum schemas Status: StatusSchema, Priority: PrioritySchema, // Value object schemas IdValueObject: IdValueObjectSchema, TimestampValueObject: TimestampValueObjectSchema, MoneyValueObject: MoneyValueObjectSchema, // Common schemas Pagination: PaginationSchema, Sort: SortSchema, Filter: FilterSchema, Search: SearchSchema, ApiResponse: ApiResponseSchema, PaginatedResponse: PaginatedResponseSchema, ErrorResponse: ErrorResponseSchema, // Validation schemas ValidationError: ValidationErrorSchema, ValidationResult: ValidationResultSchema, // Audit schemas AuditMetadata: AuditMetadataSchema, AuditLog: AuditLogSchema, // File schemas File: FileSchema, FileUpload: FileUploadSchema, // Notification schemas Notification: NotificationSchema, // Settings schemas Settings: SettingsSchema, // Utilities ValidationUtils: SharedValidationUtils }; //# sourceMappingURL=shared-schemas.js.map