UNPKG

@earendil-works/pi-coding-agent

Version:

Coding agent CLI with read, bash, edit, write tools and session management

110 lines 3.94 kB
import { open } from "node:fs/promises"; const IMAGE_TYPE_SNIFF_BYTES = 4100; const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]; export function detectSupportedImageMimeType(buffer) { if (startsWith(buffer, [0xff, 0xd8, 0xff])) { return buffer[3] === 0xf7 ? null : "image/jpeg"; } if (startsWith(buffer, PNG_SIGNATURE)) { return isPng(buffer) && !isAnimatedPng(buffer) ? "image/png" : null; } if (startsWithAscii(buffer, 0, "GIF")) { return "image/gif"; } if (startsWithAscii(buffer, 0, "RIFF") && startsWithAscii(buffer, 8, "WEBP")) { return "image/webp"; } if (startsWithAscii(buffer, 0, "BM") && isBmp(buffer)) { return "image/bmp"; } return null; } export async function detectSupportedImageMimeTypeFromFile(filePath) { const fileHandle = await open(filePath, "r"); try { const buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES); const { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0); return detectSupportedImageMimeType(buffer.subarray(0, bytesRead)); } finally { await fileHandle.close(); } } function isPng(buffer) { return (buffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, "IHDR")); } function isAnimatedPng(buffer) { let offset = PNG_SIGNATURE.length; while (offset + 8 <= buffer.length) { const chunkLength = readUint32BE(buffer, offset); const chunkTypeOffset = offset + 4; if (startsWithAscii(buffer, chunkTypeOffset, "acTL")) return true; if (startsWithAscii(buffer, chunkTypeOffset, "IDAT")) return false; const nextOffset = offset + 8 + chunkLength + 4; if (nextOffset <= offset || nextOffset > buffer.length) return false; offset = nextOffset; } return false; } function isBmp(buffer) { if (buffer.length < 26) return false; const declaredFileSize = readUint32LE(buffer, 2); const pixelDataOffset = readUint32LE(buffer, 10); const dibHeaderSize = readUint32LE(buffer, 14); if (declaredFileSize !== 0 && declaredFileSize < 26) return false; if (pixelDataOffset < 14 + dibHeaderSize) return false; if (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize) return false; let colorPlanes; let bitsPerPixel; if (dibHeaderSize === 12) { colorPlanes = readUint16LE(buffer, 22); bitsPerPixel = readUint16LE(buffer, 24); } else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) { if (buffer.length < 30) return false; colorPlanes = readUint16LE(buffer, 26); bitsPerPixel = readUint16LE(buffer, 28); } else { return false; } return colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel); } function readUint16LE(buffer, offset) { return (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8); } function readUint32BE(buffer, offset) { return ((buffer[offset] ?? 0) * 0x1000000 + ((buffer[offset + 1] ?? 0) << 16) + ((buffer[offset + 2] ?? 0) << 8) + (buffer[offset + 3] ?? 0)); } function readUint32LE(buffer, offset) { return ((buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8) + ((buffer[offset + 2] ?? 0) << 16) + (buffer[offset + 3] ?? 0) * 0x1000000); } function startsWith(buffer, bytes) { if (buffer.length < bytes.length) return false; return bytes.every((byte, index) => buffer[index] === byte); } function startsWithAscii(buffer, offset, text) { if (buffer.length < offset + text.length) return false; for (let index = 0; index < text.length; index++) { if (buffer[offset + index] !== text.charCodeAt(index)) return false; } return true; } //# sourceMappingURL=mime.js.map