( filePath: string, cwd: string, )
| 185 | * Includes automatic downsampling for large images. |
| 186 | */ |
| 187 | export async function processImageFile( |
| 188 | filePath: string, |
| 189 | cwd: string, |
| 190 | ): Promise<ImageUploadResult> { |
| 191 | const resolvedPath = resolveFilePath(filePath, cwd) |
| 192 | |
| 193 | // Validate file exists |
| 194 | let stats |
| 195 | try { |
| 196 | stats = statSync(resolvedPath) |
| 197 | } catch (error) { |
| 198 | logger.debug({ resolvedPath, error }, 'Image handler: File not found') |
| 199 | return { success: false, error: `File not found: ${filePath}` } |
| 200 | } |
| 201 | |
| 202 | if (!stats.isFile()) { |
| 203 | return { success: false, error: `Path is not a file: ${filePath}` } |
| 204 | } |
| 205 | |
| 206 | // Validate file size |
| 207 | if (stats.size > MAX_IMAGE_FILE_SIZE) { |
| 208 | const sizeMB = (stats.size / (1024 * 1024)).toFixed(1) |
| 209 | const maxMB = (MAX_IMAGE_FILE_SIZE / (1024 * 1024)).toFixed(1) |
| 210 | return { success: false, error: `File too large: ${sizeMB}MB (max ${maxMB}MB): ${filePath}` } |
| 211 | } |
| 212 | |
| 213 | // Validate image format |
| 214 | if (!isImageFile(resolvedPath)) { |
| 215 | return { |
| 216 | success: false, |
| 217 | error: `Unsupported image format: ${filePath}. Supported: ${Array.from(SUPPORTED_IMAGE_EXTENSIONS).join(', ')}`, |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | // Get MIME type |
| 222 | const mediaType = getImageMimeType(path.extname(resolvedPath)) |
| 223 | if (!mediaType) { |
| 224 | return { success: false, error: `Could not determine image type for: ${filePath}` } |
| 225 | } |
| 226 | |
| 227 | // Read file |
| 228 | let fileBuffer: Buffer |
| 229 | try { |
| 230 | fileBuffer = readFileSync(resolvedPath) |
| 231 | } catch (error) { |
| 232 | logger.debug({ resolvedPath, error }, 'Image handler: Failed to read file') |
| 233 | return { success: false, error: `Could not read file: ${filePath}` } |
| 234 | } |
| 235 | |
| 236 | // Get initial dimensions |
| 237 | let width: number | undefined |
| 238 | let height: number | undefined |
| 239 | try { |
| 240 | const image = await Jimp.read(fileBuffer) |
| 241 | width = image.bitmap.width |
| 242 | height = image.bitmap.height |
| 243 | } catch { |
| 244 | // Continue without dimensions if we can't read them |
no test coverage detected