"use strict"; const PNG_SIGNATURE = "89504e470d0a1a0a"; const HEIC_BRANDS = new Set([ "heic", "heix", "hevc", "hevx", "heim", "heis", "hevm", "hevs", "mif1", "msf1", "heif", ]); /** * Определяет тип изображения по сигнатуре первых байтов (magic bytes). * @param {Buffer} buffer * @returns {'jpeg' | 'png' | 'webp' | 'heic' | null} */ function sniffImageType(buffer) { if (!Buffer.isBuffer(buffer) || buffer.length < 12) { return null; } // JPEG: FF D8 FF if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) { return "jpeg"; } // PNG: 89 50 4E 47 0D 0A 1A 0A if (buffer.subarray(0, 8).toString("hex") === PNG_SIGNATURE) { return "png"; } // WebP: RIFF....WEBP if ( buffer.toString("ascii", 0, 4) === "RIFF" && buffer.toString("ascii", 8, 12) === "WEBP" ) { return "webp"; } // HEIC / HEIF: ftyp box if (buffer.toString("ascii", 4, 8) === "ftyp") { const majorBrand = buffer.toString("ascii", 8, 12).toLowerCase(); if (HEIC_BRANDS.has(majorBrand)) { return "heic"; } // Check compatible brands const boxLength = buffer.readUInt32BE(0); const limit = Math.min(buffer.length, boxLength > 0 ? boxLength : 64); for (let offset = 16; offset + 4 <= limit; offset += 4) { const brand = buffer.toString("ascii", offset, offset + 4).toLowerCase(); if (HEIC_BRANDS.has(brand)) { return "heic"; } } } return null; } module.exports = { sniffImageType, HEIC_BRANDS, };