import sharp from "sharp"; import { brightness } from "./operations/brightness.js"; import { colorBlindness } from "./operations/color-blindness.js"; import { colorChannels } from "./operations/color-channels.js"; import { compress } from "./operations/compress.js"; import { contrast } from "./operations/contrast.js"; import { convert } from "./operations/convert.js"; import { crop } from "./operations/crop.js"; import { editMetadata } from "./operations/edit-metadata.js"; import { flip } from "./operations/flip.js"; import { grayscale } from "./operations/grayscale.js"; import { invert } from "./operations/invert.js"; import { resize } from "./operations/resize.js"; import { rotate } from "./operations/rotate.js"; import { saturation } from "./operations/saturation.js"; import { sepia } from "./operations/sepia.js"; import { sharpen, sharpenAdvanced } from "./operations/sharpen.js"; import { stripMetadata } from "./operations/strip-metadata.js"; import type { BrightnessOptions, ColorBlindnessOptions, ColorChannelOptions, CompressOptions, ContrastOptions, ConvertOptions, CropOptions, EditMetadataOptions, FlipOptions, OperationResult, OutputFormat, ResizeOptions, RotateOptions, SaturationOptions, Sharp, SharpenAdvancedOptions, SharpenOptions, StripMetadataOptions, } from "./types.js"; import { getImageInfo } from "./utils/metadata.js"; export interface Operation { type: string; options: Record; } const OPERATION_MAP: Record< string, (image: Sharp, options: Record) => Promise > = { resize: (img, opts) => resize(img, opts as unknown as ResizeOptions), crop: (img, opts) => crop(img, opts as unknown as CropOptions), rotate: (img, opts) => rotate(img, opts as unknown as RotateOptions), flip: (img, opts) => flip(img, opts as unknown as FlipOptions), convert: (img, opts) => convert(img, opts as unknown as ConvertOptions), compress: (img, opts) => compress(img, opts as unknown as CompressOptions), "strip-metadata": (img, opts) => stripMetadata(img, opts as unknown as StripMetadataOptions), brightness: (img, opts) => brightness(img, opts as unknown as BrightnessOptions), contrast: (img, opts) => contrast(img, opts as unknown as ContrastOptions), saturation: (img, opts) => saturation(img, opts as unknown as SaturationOptions), "color-blindness": (img, opts) => colorBlindness(img, opts as unknown as ColorBlindnessOptions), "color-channels": (img, opts) => colorChannels(img, opts as unknown as ColorChannelOptions), grayscale: (img) => grayscale(img), sepia: (img) => sepia(img), sharpen: (img, opts) => sharpen(img, opts as unknown as SharpenOptions), "sharpen-advanced": (img, opts) => sharpenAdvanced(img, opts as unknown as SharpenAdvancedOptions), invert: (img) => invert(img), "edit-metadata": (img, opts) => editMetadata(img, opts as unknown as EditMetadataOptions), }; const FORMAT_MAP: Record = { jpg: "jpeg", png: "png", webp: "webp", avif: "avif", heif: "avif", tiff: "tiff", gif: "gif", jxl: "jxl", }; /** * Process an image through a pipeline of operations. * * @param input - The raw image buffer * @param operations - Array of operations to apply in sequence * @param outputFormat - Optional output format (defaults to input format) * @returns The processed image buffer and metadata */ export async function processImage( input: Buffer, operations: Operation[], outputFormat?: OutputFormat, ): Promise { let image: Sharp = sharp(input); // Apply each operation in sequence for (const op of operations) { const handler = OPERATION_MAP[op.type]; if (!handler) { throw new Error(`Unknown operation: ${op.type}`); } image = await handler(image, op.options); } // Convert to output format if specified if (outputFormat) { const sharpFormat = FORMAT_MAP[outputFormat]; if (!sharpFormat) { throw new Error(`Unsupported output format: ${outputFormat}`); } image = image.toFormat(sharpFormat as keyof sharp.FormatEnum); } const buffer = await image.toBuffer(); const info = await getImageInfo(buffer); return { buffer, info }; }