UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

214 lines (213 loc) 7.17 kB
import { o as mediaKindFromMime } from "./constants-Mf57IYS0.js"; import path from "node:path"; //#region packages/media-core/src/lazy-import.ts /** Creates a single-flight promise cache around a lazy import or other async loader. */ function createLazyImportLoader(load, options = {}) { let promise; const createPromise = () => { const loaded = Promise.resolve().then(load); if (options.cacheRejections !== true) loaded.catch(() => { if (promise === loaded) promise = void 0; }); return loaded; }; return { async load() { promise ??= createPromise(); return await promise; }, clear() { promise = void 0; } }; } //#endregion //#region packages/media-core/src/mime.ts /** Maximum byte prefix passed to dependency MIME sniffers for bounded memory/CPU work. */ const FILE_TYPE_SNIFF_MAX_BYTES = 1024 * 1024; const EXT_BY_MIME = { "image/heic": ".heic", "image/heif": ".heif", "image/bmp": ".bmp", "image/jpg": ".jpg", "image/jpeg": ".jpg", "image/png": ".png", "image/svg+xml": ".svg", "image/webp": ".webp", "image/gif": ".gif", "audio/ogg": ".ogg", "audio/mpeg": ".mp3", "audio/mp3": ".mp3", "audio/wav": ".wav", "audio/wave": ".wav", "audio/x-wav": ".wav", "audio/flac": ".flac", "audio/aac": ".aac", "audio/opus": ".opus", "audio/webm": ".webm", "audio/x-m4a": ".m4a", "audio/mp4": ".m4a", "audio/x-caf": ".caf", "video/x-msvideo": ".avi", "video/mp4": ".mp4", "video/x-matroska": ".mkv", "video/webm": ".webm", "video/x-flv": ".flv", "video/x-ms-wmv": ".wmv", "video/quicktime": ".mov", "application/pdf": ".pdf", "application/json": ".json", "application/yaml": ".yaml", "application/zip": ".zip", "application/gzip": ".gz", "application/x-tar": ".tar", "application/x-7z-compressed": ".7z", "application/vnd.rar": ".rar", "application/msword": ".doc", "application/vnd.ms-excel": ".xls", "application/vnd.ms-powerpoint": ".ppt", "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx", "application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx", "text/csv": ".csv", "text/plain": ".txt", "text/markdown": ".md", "text/html": ".html", "text/xml": ".xml", "text/css": ".css", "application/xml": ".xml" }; function buildMimeByExt() { const byExt = {}; for (const [mime, ext] of Object.entries(EXT_BY_MIME)) byExt[ext] ??= mime; return byExt; } const MIME_BY_EXT = { ...buildMimeByExt(), ".jpg": "image/jpeg", ".mp3": "audio/mpeg", ".wav": "audio/wav", ".webm": "video/webm", ".jpeg": "image/jpeg", ".js": "text/javascript", ".log": "text/plain", ".htm": "text/html", ".xml": "text/xml", ".yml": "application/yaml" }; const AUDIO_FILE_EXTENSIONS = new Set([ ".aac", ".caf", ".flac", ".m4a", ".mp3", ".oga", ".ogg", ".opus", ".wav" ]); const fileTypeModuleLoader = createLazyImportLoader(() => import("file-type")); /** Normalizes MIME strings by dropping parameters, lowercasing, and folding APNG to PNG. */ function normalizeMimeType(mime) { if (!mime) return; const cleaned = mime.split(";")[0]?.trim().toLowerCase(); if (cleaned === "image/apng") return "image/png"; return cleaned || void 0; } /** Returns the bounded buffer prefix used for dependency MIME sniffing. */ function sliceMimeSniffBuffer(buffer) { if (buffer.byteLength <= 1048576) return buffer; return buffer.subarray(0, FILE_TYPE_SNIFF_MAX_BYTES); } async function sniffMime(buffer) { if (!buffer) return; try { const { fileTypeFromBuffer } = await fileTypeModuleLoader.load(); const type = await fileTypeFromBuffer(sliceMimeSniffBuffer(buffer)); if (type?.mime) return normalizeMimeType(type.mime); } catch {} return sniffKnownAudioMagic(buffer); } function sniffKnownAudioMagic(buffer) { if (buffer.byteLength >= 4 && buffer.toString("ascii", 0, 4) === "caff") return "audio/x-caf"; } /** Extracts a lowercase extension from a local path or HTTP URL pathname. */ function getFileExtension(filePath) { if (!filePath) return; try { if (/^https?:\/\//i.test(filePath)) { const url = new URL(filePath); return path.extname(url.pathname).toLowerCase() || void 0; } } catch {} return path.extname(filePath).toLowerCase() || void 0; } /** Maps a file path or URL extension to the preferred MIME type when known. */ function mimeTypeFromFilePath(filePath) { const ext = getFileExtension(filePath); if (!ext) return; return MIME_BY_EXT[ext]; } /** Returns true when a filename extension is a supported audio container. */ function isAudioFileName(fileName) { const ext = getFileExtension(fileName); if (!ext) return false; return AUDIO_FILE_EXTENSIONS.has(ext); } /** Detects the best MIME type from bytes, file path, and header metadata. */ function detectMime(opts) { return detectMimeImpl(opts); } function isGenericMime(mime) { if (!mime) return true; const m = mime.toLowerCase(); return m === "application/octet-stream" || m === "application/zip"; } function isImageMime(mime) { return mediaKindFromMime(normalizeMimeType(mime)) === "image"; } async function detectMimeImpl(opts) { const ext = getFileExtension(opts.filePath); const extMime = ext ? MIME_BY_EXT[ext] : void 0; const headerMime = normalizeMimeType(opts.headerMime); const sniffed = await sniffMime(opts.buffer); const sniffedGenericContainer = sniffed && isGenericMime(sniffed); const trustedExtMime = sniffedGenericContainer && isImageMime(extMime) ? void 0 : extMime; const trustedHeaderMime = sniffedGenericContainer && isImageMime(headerMime) ? void 0 : headerMime; if (sniffed && (!isGenericMime(sniffed) || !trustedExtMime)) return sniffed; if (trustedExtMime) return trustedExtMime; if (trustedHeaderMime && !isGenericMime(trustedHeaderMime)) return trustedHeaderMime; if (sniffed) return sniffed; if (trustedHeaderMime) return trustedHeaderMime; } /** Returns the preferred file extension for a normalized or raw MIME string. */ function extensionForMime(mime) { const normalized = normalizeMimeType(mime); if (!normalized) return; return EXT_BY_MIME[normalized]; } /** Returns true when content type or filename identifies GIF media. */ function isGifMedia(opts) { if (opts.contentType?.toLowerCase() === "image/gif") return true; return getFileExtension(opts.fileName) === ".gif"; } /** Maps image format labels from encoders/probes to MIME types. */ function imageMimeFromFormat(format) { if (!format) return; switch (format.toLowerCase()) { case "jpg": case "jpeg": return "image/jpeg"; case "heic": return "image/heic"; case "heif": return "image/heif"; case "png": return "image/png"; case "webp": return "image/webp"; case "gif": return "image/gif"; default: return; } } /** Normalizes a MIME string before classifying it into a media family. */ function kindFromMime(mime) { return mediaKindFromMime(normalizeMimeType(mime)); } //#endregion export { imageMimeFromFormat as a, kindFromMime as c, sliceMimeSniffBuffer as d, getFileExtension as i, mimeTypeFromFilePath as l, detectMime as n, isAudioFileName as o, extensionForMime as r, isGifMedia as s, FILE_TYPE_SNIFF_MAX_BYTES as t, normalizeMimeType as u };