zentrixui
Version:
ZentrixUI - A modern, highly customizable and accessible React file upload component library with multiple variants, JSON-based configuration, and excellent developer experience.
1 lines • 43.5 kB
Source Map (JSON)
{"version":3,"file":"utils.cjs","sources":["../src/lib/utils/error-handling.ts","../src/lib/utils/file-validation.ts"],"sourcesContent":["import { FileError, FileUploadConfig } from '../components/file-upload/file-upload.types'\r\n\r\nexport type ErrorType =\r\n | 'validation'\r\n | 'network'\r\n | 'upload'\r\n | 'file-system'\r\n | 'permission'\r\n | 'quota'\r\n | 'timeout'\r\n | 'unknown'\r\n\r\nexport interface ErrorContext {\r\n fileName?: string\r\n fileSize?: number\r\n fileType?: string\r\n operation?: string\r\n timestamp?: Date\r\n userAgent?: string\r\n retryCount?: number\r\n maxRetries?: number\r\n}\r\n\r\nexport interface ProcessedError {\r\n id: string\r\n type: ErrorType\r\n code: string\r\n title: string\r\n message: string\r\n userMessage: string\r\n technicalMessage: string\r\n severity: 'low' | 'medium' | 'high' | 'critical'\r\n recoverable: boolean\r\n retryable: boolean\r\n context: ErrorContext\r\n suggestions: string[]\r\n actions: ErrorAction[]\r\n}\r\n\r\nexport interface ErrorAction {\r\n id: string\r\n label: string\r\n type: 'retry' | 'remove' | 'clear' | 'refresh' | 'contact' | 'custom'\r\n handler?: () => void | Promise<void>\r\n disabled?: boolean\r\n primary?: boolean\r\n}\r\n\r\n/**\r\n * Maps common error codes to user-friendly messages\r\n */\r\nconst ERROR_MESSAGES: Record<string, {\r\n title: string\r\n userMessage: string\r\n severity: ProcessedError['severity']\r\n recoverable: boolean\r\n retryable: boolean\r\n suggestions: string[]\r\n}> = {\r\n // File validation errors\r\n 'file-too-large': {\r\n title: 'File Too Large',\r\n userMessage: 'The selected file is too large to upload.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Try compressing the file before uploading',\r\n 'Choose a smaller file',\r\n 'Contact support if you need to upload larger files'\r\n ]\r\n },\r\n 'file-too-small': {\r\n title: 'File Too Small',\r\n userMessage: 'The selected file is too small.',\r\n severity: 'low',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Make sure the file contains content',\r\n 'Choose a different file'\r\n ]\r\n },\r\n 'invalid-file-type': {\r\n title: 'Invalid File Type',\r\n userMessage: 'This file type is not supported.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Check the list of supported file types',\r\n 'Convert your file to a supported format',\r\n 'Choose a different file'\r\n ]\r\n },\r\n 'invalid-file-extension': {\r\n title: 'Invalid File Extension',\r\n userMessage: 'This file extension is not allowed.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Rename the file with a supported extension',\r\n 'Convert your file to a supported format'\r\n ]\r\n },\r\n 'too-many-files': {\r\n title: 'Too Many Files',\r\n userMessage: 'You have selected too many files.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Remove some files and try again',\r\n 'Upload files in smaller batches'\r\n ]\r\n },\r\n 'image-width-too-large': {\r\n title: 'Image Too Wide',\r\n userMessage: 'The image width exceeds the maximum allowed size.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Resize the image to a smaller width',\r\n 'Use an image editing tool to reduce dimensions'\r\n ]\r\n },\r\n 'image-height-too-large': {\r\n title: 'Image Too Tall',\r\n userMessage: 'The image height exceeds the maximum allowed size.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Resize the image to a smaller height',\r\n 'Use an image editing tool to reduce dimensions'\r\n ]\r\n },\r\n 'invalid-image': {\r\n title: 'Invalid Image',\r\n userMessage: 'The image file appears to be corrupted or invalid.',\r\n severity: 'high',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Try opening the image in an image viewer to verify it works',\r\n 'Choose a different image file',\r\n 'Re-save the image in a different format'\r\n ]\r\n },\r\n\r\n // Network and upload errors\r\n 'network-error': {\r\n title: 'Network Error',\r\n userMessage: 'Unable to connect to the server.',\r\n severity: 'high',\r\n recoverable: true,\r\n retryable: true,\r\n suggestions: [\r\n 'Check your internet connection',\r\n 'Try again in a few moments',\r\n 'Contact support if the problem persists'\r\n ]\r\n },\r\n 'upload-timeout': {\r\n title: 'Upload Timeout',\r\n userMessage: 'The upload took too long and was cancelled.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: true,\r\n suggestions: [\r\n 'Try uploading a smaller file',\r\n 'Check your internet connection speed',\r\n 'Try again when your connection is more stable'\r\n ]\r\n },\r\n 'server-error': {\r\n title: 'Server Error',\r\n userMessage: 'The server encountered an error while processing your upload.',\r\n severity: 'high',\r\n recoverable: true,\r\n retryable: true,\r\n suggestions: [\r\n 'Try again in a few minutes',\r\n 'Contact support if the error continues'\r\n ]\r\n },\r\n 'quota-exceeded': {\r\n title: 'Storage Quota Exceeded',\r\n userMessage: 'You have reached your storage limit.',\r\n severity: 'high',\r\n recoverable: false,\r\n retryable: false,\r\n suggestions: [\r\n 'Delete some existing files to free up space',\r\n 'Upgrade your storage plan',\r\n 'Contact support for assistance'\r\n ]\r\n },\r\n 'permission-denied': {\r\n title: 'Permission Denied',\r\n userMessage: 'You do not have permission to upload files.',\r\n severity: 'high',\r\n recoverable: false,\r\n retryable: false,\r\n suggestions: [\r\n 'Contact your administrator for access',\r\n 'Make sure you are logged in with the correct account'\r\n ]\r\n },\r\n\r\n // File system errors\r\n 'file-read-error': {\r\n title: 'Cannot Read File',\r\n userMessage: 'Unable to read the selected file.',\r\n severity: 'high',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Make sure the file is not corrupted',\r\n 'Try selecting the file again',\r\n 'Choose a different file'\r\n ]\r\n },\r\n 'file-access-denied': {\r\n title: 'File Access Denied',\r\n userMessage: 'Cannot access the selected file.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: false,\r\n suggestions: [\r\n 'Make sure the file is not open in another application',\r\n 'Check file permissions',\r\n 'Try copying the file to a different location first'\r\n ]\r\n },\r\n\r\n // Generic errors\r\n 'unknown-error': {\r\n title: 'Unknown Error',\r\n userMessage: 'An unexpected error occurred.',\r\n severity: 'medium',\r\n recoverable: true,\r\n retryable: true,\r\n suggestions: [\r\n 'Try the operation again',\r\n 'Refresh the page if the problem persists',\r\n 'Contact support if you continue to experience issues'\r\n ]\r\n }\r\n}\r\n\r\n/**\r\n * Processes a raw error into a user-friendly format\r\n */\r\nexport const processError = (\r\n error: Error | FileError | string,\r\n context: ErrorContext = {},\r\n config?: FileUploadConfig\r\n): ProcessedError => {\r\n const timestamp = new Date()\r\n const errorId = `error_${timestamp.getTime()}_${Math.random().toString(36).substring(2, 11)}`\r\n\r\n let code: string\r\n let technicalMessage: string\r\n let type: ErrorType = 'unknown'\r\n\r\n // Extract error information based on error type\r\n if (typeof error === 'string') {\r\n code = 'unknown-error'\r\n technicalMessage = error\r\n } else if (error && typeof error === 'object' && 'code' in error && 'type' in error) {\r\n // FileError from validation\r\n code = error.code\r\n technicalMessage = error.message || 'Validation error occurred'\r\n type = error.type === 'size' ? 'validation' :\r\n error.type === 'type' ? 'validation' :\r\n error.type === 'count' ? 'validation' :\r\n error.type === 'dimensions' ? 'validation' :\r\n error.type === 'network' ? 'network' :\r\n 'validation'\r\n } else if (error && typeof error === 'object' && 'code' in error) {\r\n // Malformed FileError with only code\r\n code = error.code as string\r\n technicalMessage = (error as any).message || 'Error occurred'\r\n } else if (error instanceof Error) {\r\n // Standard JavaScript Error\r\n code = error.name.toLowerCase().replace(/error$/, '') || 'unknown-error'\r\n technicalMessage = error.message\r\n\r\n // Determine error type based on error properties\r\n if (error.message.includes('network') || error.message.includes('fetch')) {\r\n type = 'network'\r\n } else if (error.message.includes('timeout')) {\r\n type = 'timeout'\r\n } else if (error.message.includes('permission') || error.message.includes('denied')) {\r\n type = 'permission'\r\n } else if (error.message.includes('quota') || error.message.includes('storage')) {\r\n type = 'quota'\r\n }\r\n } else if (error === null || error === undefined) {\r\n code = 'unknown-error'\r\n technicalMessage = 'Unknown error occurred'\r\n } else {\r\n code = 'unknown-error'\r\n technicalMessage = 'Unknown error occurred'\r\n }\r\n\r\n // Get error details from the mapping\r\n const errorDetails = ERROR_MESSAGES[code] || ERROR_MESSAGES['unknown-error']\r\n\r\n // Create user message with context\r\n let userMessage = errorDetails.userMessage\r\n if (context.fileName) {\r\n userMessage = `${userMessage} (File: ${context.fileName})`\r\n }\r\n\r\n // Generate suggestions based on context and config\r\n const suggestions = [...errorDetails.suggestions]\r\n if (config && context.fileName) {\r\n if (code === 'file-too-large' && config.validation.maxSize) {\r\n const maxSizeMB = (config.validation.maxSize / 1024 / 1024).toFixed(1)\r\n suggestions.unshift(`Maximum file size is ${maxSizeMB} MB`)\r\n }\r\n if (code === 'invalid-file-type' && config.validation.allowedTypes.length > 0) {\r\n suggestions.unshift(`Supported types: ${config.validation.allowedTypes.join(', ')}`)\r\n }\r\n }\r\n\r\n // Generate recovery actions\r\n const actions: ErrorAction[] = []\r\n\r\n if (errorDetails.retryable) {\r\n actions.push({\r\n id: 'retry',\r\n label: config?.labels.retryText || 'Try Again',\r\n type: 'retry',\r\n primary: true\r\n })\r\n }\r\n\r\n if (errorDetails.recoverable) {\r\n actions.push({\r\n id: 'remove',\r\n label: config?.labels.removeText || 'Remove File',\r\n type: 'remove'\r\n })\r\n }\r\n\r\n actions.push({\r\n id: 'clear',\r\n label: 'Clear All',\r\n type: 'clear'\r\n })\r\n\r\n if (errorDetails.severity === 'high' || errorDetails.severity === 'critical') {\r\n actions.push({\r\n id: 'contact',\r\n label: 'Contact Support',\r\n type: 'contact'\r\n })\r\n }\r\n\r\n return {\r\n id: errorId,\r\n type,\r\n code,\r\n title: errorDetails.title,\r\n message: technicalMessage,\r\n userMessage,\r\n technicalMessage,\r\n severity: errorDetails.severity,\r\n recoverable: errorDetails.recoverable,\r\n retryable: errorDetails.retryable,\r\n context: {\r\n ...context,\r\n timestamp,\r\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined\r\n },\r\n suggestions,\r\n actions\r\n }\r\n}\r\n\r\n/**\r\n * Processes multiple errors and groups them by type\r\n */\r\nexport const processErrors = (\r\n errors: (Error | FileError | string)[],\r\n context: ErrorContext = {},\r\n config?: FileUploadConfig\r\n): {\r\n errors: ProcessedError[]\r\n summary: {\r\n total: number\r\n byType: Record<ErrorType, number>\r\n bySeverity: Record<ProcessedError['severity'], number>\r\n retryable: number\r\n recoverable: number\r\n }\r\n} => {\r\n const processedErrors = errors.map(error => processError(error, context, config))\r\n\r\n const summary = {\r\n total: processedErrors.length,\r\n byType: {} as Record<ErrorType, number>,\r\n bySeverity: {} as Record<ProcessedError['severity'], number>,\r\n retryable: 0,\r\n recoverable: 0\r\n }\r\n\r\n processedErrors.forEach(error => {\r\n // Count by type\r\n summary.byType[error.type] = (summary.byType[error.type] || 0) + 1\r\n\r\n // Count by severity\r\n summary.bySeverity[error.severity] = (summary.bySeverity[error.severity] || 0) + 1\r\n\r\n // Count retryable and recoverable\r\n if (error.retryable) summary.retryable++\r\n if (error.recoverable) summary.recoverable++\r\n })\r\n\r\n return {\r\n errors: processedErrors,\r\n summary\r\n }\r\n}\r\n\r\n/**\r\n * Creates a user-friendly error message for display\r\n */\r\nexport const formatErrorForUser = (\r\n error: ProcessedError,\r\n includeContext: boolean = true\r\n): string => {\r\n let message = error.userMessage\r\n\r\n if (includeContext && error.context.fileName) {\r\n message += ` (${error.context.fileName})`\r\n }\r\n\r\n if (error.suggestions.length > 0) {\r\n message += `\\n\\nSuggestions:\\n${error.suggestions.map(s => `• ${s}`).join('\\n')}`\r\n }\r\n\r\n return message\r\n}\r\n\r\n/**\r\n * Determines if an error should trigger an accessibility announcement\r\n */\r\nexport const shouldAnnounceError = (error: ProcessedError): boolean => {\r\n return error.severity === 'high' || error.severity === 'critical' || !error.recoverable\r\n}\r\n\r\n/**\r\n * Creates an accessibility-friendly error announcement\r\n */\r\nexport const createErrorAnnouncement = (error: ProcessedError): string => {\r\n const severity = error.severity === 'critical' ? 'Critical error: ' :\r\n error.severity === 'high' ? 'Error: ' : ''\r\n\r\n return `${severity}${error.title}. ${error.userMessage}`\r\n}\r\n\r\n/**\r\n * Logs error for debugging and monitoring\r\n */\r\nexport const logError = (\r\n error: ProcessedError,\r\n additionalContext?: Record<string, any>\r\n): void => {\r\n const logData = {\r\n errorId: error.id,\r\n type: error.type,\r\n code: error.code,\r\n severity: error.severity,\r\n message: error.technicalMessage,\r\n context: error.context,\r\n ...additionalContext\r\n }\r\n\r\n if (error.severity === 'critical') {\r\n console.error('Critical FileUpload Error:', logData)\r\n } else if (error.severity === 'high') {\r\n console.error('FileUpload Error:', logData)\r\n } else {\r\n console.warn('FileUpload Warning:', logData)\r\n }\r\n\r\n // In a real application, you might want to send this to an error tracking service\r\n // Example: errorTrackingService.captureError(logData)\r\n}","import { FileError, FileValidationResult, BatchValidationResult, RejectedFile, FileUploadConfig } from '../components/file-upload/file-upload.types'\r\n\r\n/**\r\n * Utility function to get file extension from filename\r\n */\r\nexport const getFileExtension = (filename: string): string => {\r\n const lastDotIndex = filename.lastIndexOf('.')\r\n if (lastDotIndex === -1) return ''\r\n return filename.substring(lastDotIndex + 1).toLowerCase()\r\n}\r\n\r\n/**\r\n * Validates a single file against type restrictions\r\n * Supports MIME types, file extensions, and wildcard patterns\r\n */\r\nexport const validateFileType = (\r\n file: File,\r\n allowedTypes: string[] = ['*'],\r\n allowedExtensions: string[] = ['*']\r\n): FileValidationResult => {\r\n const errors: FileError[] = []\r\n const warnings: string[] = []\r\n\r\n const fileType = file.type\r\n const fileName = file.name\r\n const fileExtension = getFileExtension(fileName)\r\n\r\n // Add warning for files without extensions\r\n if (!fileExtension && fileName.indexOf('.') === -1) {\r\n warnings.push('File has no extension, type validation may be unreliable')\r\n }\r\n\r\n // If wildcard is present, allow all types\r\n if (allowedTypes.includes('*') && allowedExtensions.includes('*')) {\r\n return { isValid: true, errors, warnings }\r\n }\r\n\r\n // Validate MIME types\r\n if (!allowedTypes.includes('*')) {\r\n let typeValid = false\r\n\r\n for (const allowedType of allowedTypes) {\r\n if (allowedType === '*') {\r\n typeValid = true\r\n break\r\n }\r\n\r\n // Support wildcard patterns like \"image/*\", \"video/*\"\r\n if (allowedType.endsWith('/*')) {\r\n const baseType = allowedType.slice(0, -2)\r\n if (fileType.startsWith(baseType + '/')) {\r\n typeValid = true\r\n break\r\n }\r\n }\r\n\r\n // Exact MIME type match\r\n if (fileType === allowedType) {\r\n typeValid = true\r\n break\r\n }\r\n }\r\n\r\n if (!typeValid) {\r\n errors.push({\r\n code: 'invalid-file-type',\r\n message: `File type \"${fileType}\" is not allowed. Allowed types: ${allowedTypes.join(', ')}`,\r\n type: 'type'\r\n })\r\n }\r\n }\r\n\r\n // Validate file extensions\r\n if (!allowedExtensions.includes('*')) {\r\n const extensionValid = allowedExtensions.some(ext =>\r\n ext === '*' || ext.toLowerCase() === fileExtension\r\n )\r\n\r\n if (!extensionValid) {\r\n errors.push({\r\n code: 'invalid-file-extension',\r\n message: `File extension \".${fileExtension}\" is not allowed. Allowed extensions: ${allowedExtensions.join(', ')}`,\r\n type: 'type'\r\n })\r\n }\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n errors,\r\n warnings\r\n }\r\n}\r\n\r\n/**\r\n * Utility function to format file size in human-readable format\r\n */\r\nexport const formatFileSize = (bytes: number): string => {\r\n if (bytes === 0) return '0 Bytes'\r\n\r\n const k = 1024\r\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']\r\n const i = Math.floor(Math.log(bytes) / Math.log(k))\r\n\r\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]\r\n}\r\n\r\n/**\r\n * Validates file size against minimum and maximum limits\r\n */\r\nexport const validateFileSize = (\r\n file: File,\r\n maxSize: number,\r\n minSize: number = 0\r\n): FileValidationResult => {\r\n const errors: FileError[] = []\r\n const warnings: string[] = []\r\n\r\n if (file.size > maxSize) {\r\n errors.push({\r\n code: 'file-too-large',\r\n message: `File size ${formatFileSize(file.size)} exceeds maximum allowed size of ${formatFileSize(maxSize)}`,\r\n type: 'size'\r\n })\r\n }\r\n\r\n if (file.size < minSize) {\r\n errors.push({\r\n code: 'file-too-small',\r\n message: `File size ${formatFileSize(file.size)} is below minimum required size of ${formatFileSize(minSize)}`,\r\n type: 'size'\r\n })\r\n }\r\n\r\n // Warning for very large files\r\n if (file.size > maxSize * 0.8) {\r\n warnings.push('File is approaching the maximum size limit')\r\n }\r\n\r\n // Warning for empty files\r\n if (file.size === 0) {\r\n warnings.push('File appears to be empty')\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n errors,\r\n warnings\r\n }\r\n}\r\n\r\n/**\r\n * Validates the number of files against the maximum limit\r\n */\r\nexport const validateFileCount = (\r\n files: File[],\r\n maxFiles: number,\r\n currentFileCount: number = 0\r\n): FileValidationResult => {\r\n const errors: FileError[] = []\r\n const warnings: string[] = []\r\n const totalFiles = files.length + currentFileCount\r\n\r\n if (totalFiles > maxFiles) {\r\n errors.push({\r\n code: 'too-many-files',\r\n message: `Cannot upload ${totalFiles} files. Maximum allowed is ${maxFiles}`,\r\n type: 'count'\r\n })\r\n }\r\n\r\n // Warning when approaching limit\r\n if (totalFiles > maxFiles * 0.8) {\r\n warnings.push(`Approaching file limit (${totalFiles}/${maxFiles})`)\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n errors,\r\n warnings\r\n }\r\n}\r\n\r\n/**\r\n * Validates image dimensions for image files\r\n */\r\nexport const validateImageDimensions = (\r\n file: File,\r\n maxWidth?: number,\r\n maxHeight?: number,\r\n minWidth?: number,\r\n minHeight?: number\r\n): Promise<FileValidationResult> => {\r\n return new Promise((resolve) => {\r\n const errors: FileError[] = []\r\n const warnings: string[] = []\r\n\r\n // Only validate image files\r\n if (!file.type.startsWith('image/')) {\r\n resolve({ isValid: true, errors, warnings })\r\n return\r\n }\r\n\r\n const img = new Image()\r\n const url = URL.createObjectURL(file)\r\n\r\n img.onload = () => {\r\n URL.revokeObjectURL(url)\r\n\r\n const { width, height } = img\r\n\r\n if (maxWidth && width > maxWidth) {\r\n errors.push({\r\n code: 'image-width-too-large',\r\n message: `Image width ${width}px exceeds maximum allowed width of ${maxWidth}px`,\r\n type: 'dimensions'\r\n })\r\n }\r\n\r\n if (maxHeight && height > maxHeight) {\r\n errors.push({\r\n code: 'image-height-too-large',\r\n message: `Image height ${height}px exceeds maximum allowed height of ${maxHeight}px`,\r\n type: 'dimensions'\r\n })\r\n }\r\n\r\n if (minWidth && width < minWidth) {\r\n errors.push({\r\n code: 'image-width-too-small',\r\n message: `Image width ${width}px is below minimum required width of ${minWidth}px`,\r\n type: 'dimensions'\r\n })\r\n }\r\n\r\n if (minHeight && height < minHeight) {\r\n errors.push({\r\n code: 'image-height-too-small',\r\n message: `Image height ${height}px is below minimum required height of ${minHeight}px`,\r\n type: 'dimensions'\r\n })\r\n }\r\n\r\n resolve({\r\n isValid: errors.length === 0,\r\n errors,\r\n warnings\r\n })\r\n }\r\n\r\n img.onerror = () => {\r\n URL.revokeObjectURL(url)\r\n errors.push({\r\n code: 'invalid-image',\r\n message: 'Unable to read image file or file is corrupted',\r\n type: 'validation'\r\n })\r\n resolve({ isValid: false, errors, warnings })\r\n }\r\n\r\n img.src = url\r\n })\r\n}\r\n\r\n/**\r\n * Comprehensive validation for a single file using configuration\r\n */\r\nexport const validateFile = async (\r\n file: File,\r\n config: FileUploadConfig,\r\n currentFileCount: number = 0\r\n): Promise<FileValidationResult> => {\r\n const allErrors: FileError[] = []\r\n const allWarnings: string[] = []\r\n\r\n // Validate file type\r\n const typeValidation = validateFileType(\r\n file,\r\n config.validation.allowedTypes,\r\n config.validation.allowedExtensions\r\n )\r\n allErrors.push(...typeValidation.errors)\r\n allWarnings.push(...typeValidation.warnings)\r\n\r\n // Validate file size\r\n const sizeValidation = validateFileSize(\r\n file,\r\n config.validation.maxSize,\r\n config.validation.minSize || 0\r\n )\r\n allErrors.push(...sizeValidation.errors)\r\n allWarnings.push(...sizeValidation.warnings)\r\n\r\n // Validate image dimensions if enabled\r\n if (config.validation.validateDimensions && file.type.startsWith('image/')) {\r\n const dimensionValidation = await validateImageDimensions(\r\n file,\r\n config.validation.maxWidth,\r\n config.validation.maxHeight,\r\n config.validation.minWidth,\r\n config.validation.minHeight\r\n )\r\n allErrors.push(...dimensionValidation.errors)\r\n allWarnings.push(...dimensionValidation.warnings)\r\n }\r\n\r\n return {\r\n isValid: allErrors.length === 0,\r\n errors: allErrors,\r\n warnings: allWarnings\r\n }\r\n}\r\n\r\n/**\r\n * Validates multiple files and returns separated valid/rejected files\r\n */\r\nexport const validateFiles = async (\r\n files: File[],\r\n config: FileUploadConfig,\r\n currentFileCount: number = 0\r\n): Promise<BatchValidationResult> => {\r\n const validFiles: File[] = []\r\n const rejectedFiles: RejectedFile[] = []\r\n const allWarnings: string[] = []\r\n let totalSize = 0\r\n\r\n // First validate file count\r\n const countValidation = validateFileCount(files, config.validation.maxFiles, currentFileCount)\r\n if (!countValidation.isValid) {\r\n // If too many files, reject all files\r\n const countError = countValidation.errors[0]\r\n rejectedFiles.push(...files.map(file => ({\r\n file,\r\n errors: [countError]\r\n })))\r\n\r\n return {\r\n validFiles: [],\r\n rejectedFiles,\r\n totalSize: 0,\r\n warnings: countValidation.warnings\r\n }\r\n }\r\n\r\n allWarnings.push(...countValidation.warnings)\r\n\r\n // Validate each file individually\r\n for (const file of files) {\r\n const validation = await validateFile(file, config, currentFileCount + validFiles.length)\r\n\r\n if (validation.isValid) {\r\n validFiles.push(file)\r\n totalSize += file.size\r\n } else {\r\n rejectedFiles.push({\r\n file,\r\n errors: validation.errors\r\n })\r\n }\r\n\r\n allWarnings.push(...validation.warnings)\r\n }\r\n\r\n return {\r\n validFiles,\r\n rejectedFiles,\r\n totalSize,\r\n warnings: allWarnings\r\n }\r\n}\r\n\r\n/**\r\n * Utility function to check if file is an image\r\n */\r\nexport const isImageFile = (file: File): boolean => {\r\n return file.type.startsWith('image/')\r\n}\r\n\r\n/**\r\n * Utility function to check if file is a video\r\n */\r\nexport const isVideoFile = (file: File): boolean => {\r\n return file.type.startsWith('video/')\r\n}\r\n\r\n/**\r\n * Utility function to check if file is an audio file\r\n */\r\nexport const isAudioFile = (file: File): boolean => {\r\n return file.type.startsWith('audio/')\r\n}\r\n\r\n/**\r\n * Utility function to check if file is a document\r\n */\r\nexport const isDocumentFile = (file: File): boolean => {\r\n const documentTypes = [\r\n 'application/pdf',\r\n 'application/msword',\r\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\r\n 'application/vnd.ms-excel',\r\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\r\n 'application/vnd.ms-powerpoint',\r\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\r\n 'text/plain',\r\n 'text/csv',\r\n 'application/rtf'\r\n ]\r\n\r\n return documentTypes.includes(file.type)\r\n}\r\n\r\n/**\r\n * Creates a validation error with consistent formatting\r\n */\r\nexport const createValidationError = (\r\n code: string,\r\n message: string,\r\n type: FileError['type']\r\n): FileError => {\r\n return { code, message, type }\r\n}\r\n\r\n/**\r\n * Validates accept attribute format (for HTML input compatibility)\r\n */\r\nexport const validateAcceptAttribute = (accept: string): boolean => {\r\n if (!accept || accept === '*') return true\r\n\r\n // Split by comma and validate each part\r\n const parts = accept.split(',').map(part => part.trim())\r\n\r\n for (const part of parts) {\r\n // Valid formats: .ext, mime/type, mime/*\r\n if (part.startsWith('.')) {\r\n // File extension - allow alphanumeric characters\r\n if (!/^\\.[a-zA-Z0-9]+$/.test(part)) return false\r\n } else if (part.includes('/')) {\r\n // MIME type - basic validation for type/subtype format\r\n const [type, subtype] = part.split('/')\r\n if (!type || !subtype) return false\r\n // Allow alphanumeric, hyphens, and wildcards\r\n if (!/^[a-zA-Z0-9\\-]+$/.test(type)) return false\r\n if (!/^[a-zA-Z0-9\\-*]+$/.test(subtype)) return false\r\n } else {\r\n return false\r\n }\r\n }\r\n\r\n return true\r\n}"],"names":[],"mappings":"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"}