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