# sharp-adapter _(no submodules)_ ## Purpose Provides a production-grade `ImageAdapter` implementation backed by the Sharp image processing library. Sharp delivers native-speed JPEG, PNG, WebP, and AVIF encoding through libvips, making it the recommended adapter for server-side workloads where pixel-accurate compression and real resizing are required. Sharp is deliberately **not** a direct dependency of this package. The caller supplies a Sharp factory function at construction time, which eliminates the native-addon from the `@suaveplan/compress-image` dependency tree and allows the package to be used in environments where Sharp cannot be installed (e.g., edge runtimes, bundled frontends, or CI test runs). ## Features - Full `ImageAdapter` implementation using the real Sharp pipeline - JPEG progressive encoding, PNG compression levels, WebP lossless, AVIF effort tuning - Metadata stripping by default; opt-in preservation via `stripMetadata: false` - `ResizeMode` mapped to Sharp's `fit` option (cover / contain / fill / inside / outside) - Caller-supplied Sharp factory for dependency injection - Wraps all Sharp errors in `CompressImageError` with cause-chain forwarding ## Usage: Basic Example ```ts import sharp from 'sharp'; import { SharpAdapter } from '@suaveplan/compress-image/adapters/sharp'; import { createCompressor } from '@suaveplan/compress-image/compressor'; const compressor = createCompressor(new SharpAdapter(sharp)); const result = await compressor.compress(imageBuffer, { format: 'webp', quality: 75, width: 800, height: 600, }); ``` ## Usage: Advanced Example ```ts import sharp from 'sharp'; import { SharpAdapter } from '@suaveplan/compress-image/adapters/sharp'; const adapter = new SharpAdapter(sharp); // Get metadata const info = await adapter.getInfo(jpegBuffer); console.log(info.width, info.height, info.format, info.hasAlpha); // Convert JPEG to AVIF with high compression effort const result = await adapter.compress(jpegBuffer, { format: 'avif', quality: 60, effort: 6, stripMetadata: true, }); // Resize to thumbnail keeping aspect ratio (contain mode) const thumb = await adapter.resize(jpegBuffer, 128, 128, 'contain'); ``` ## API Reference ### `interface SharpLike` A minimal factory interface matching the Sharp module export signature. Implement this to inject a mock in tests. ```ts interface SharpLike { (input: Buffer | Uint8Array): SharpInstance; } ``` ### `interface SharpInstance` The fluent pipeline instance returned by `SharpLike`. Covers the methods used by `SharpAdapter`. ### `class SharpAdapter implements ImageAdapter` #### `constructor(sharp: SharpLike)` Stores the provided Sharp factory. No I/O occurs at construction time. #### `compress(input: Uint8Array, options: CompressOptions): Promise` Runs the Sharp pipeline with format conversion, quality settings, and optional resize. Returns a `CompressResult` with real output bytes and actual dimensions. Throws `CompressImageError(UNSUPPORTED_FORMAT)` for undetectable inputs; throws `CompressImageError(COMPRESS_FAILED)` for all other Sharp errors. #### `getInfo(input: Uint8Array): Promise` Calls `sharp(input).metadata()` to extract width, height, format, and alpha channel information. Throws `CompressImageError(UNSUPPORTED_FORMAT)` if Sharp returns a format not in the known set. #### `resize(input: Uint8Array, width: number, height: number, mode?: ResizeMode): Promise` Resizes using `sharp.resize()` with the mapped `fit` option. Returns a `Uint8Array` wrapping the Sharp output `Buffer`. Default mode is `'cover'`. ## Implementation Notes Format encoding options are mapped per-format: JPEG receives `quality` and `progressive`; WebP receives `quality`, `lossless`, and `effort`; AVIF receives `quality` and `effort`; PNG receives `compressionLevel` (mapped from `effort`) but no quality parameter since PNG is lossless. A second `.metadata()` pass on the output buffer is used to derive the final `width` and `height` in `CompressResult`. This adds a small overhead but guarantees accuracy after resize operations where the actual output dimensions may differ from the requested values (e.g., when using `inside` or `outside` fit modes). Sharp's `Buffer` is wrapped in `new Uint8Array(buffer)` to satisfy the `Uint8Array` contract in `CompressResult.data`, ensuring interoperability with environments that do not use Node.js `Buffer`.