From 34ec840b72c9e866731f21b9d8105fd46b87278e Mon Sep 17 00:00:00 2001 From: stirling-image Date: Mon, 13 Apr 2026 17:30:08 +0800 Subject: [PATCH] docs(api): achieve 100% endpoint coverage in OpenAPI spec and VitePress docs (#54) Add 12 previously undocumented routes to the OpenAPI 3.1 specification: content-aware-resize, edit-metadata (+ inspect), stitch, pdf-to-image (+ info, preview), gif-tools/info, remove-background/effects, preview, pipeline/tools, and pipeline/batch. Fix license from MIT to AGPL-3.0, correct DELETE /files response from 204 to 200 with body, and update VitePress API docs (rest.md tool table, ai.md model parameters). Also register the sharpen operation in the image-engine OPERATION_MAP so it can be used as a standalone pipeline step. Co-authored-by: stirling-image --- apps/api/src/openapi.yaml | 592 +++++++++++++++++++++++++++- apps/docs/api/ai.md | 66 +++- apps/docs/api/rest.md | 14 +- packages/image-engine/src/engine.ts | 3 + 4 files changed, 654 insertions(+), 21 deletions(-) diff --git a/apps/api/src/openapi.yaml b/apps/api/src/openapi.yaml index ee7c23bd..05924b80 100644 --- a/apps/api/src/openapi.yaml +++ b/apps/api/src/openapi.yaml @@ -15,7 +15,7 @@ info: Endpoints marked with a lock icon require authentication. Admin-only endpoints are noted in their description. license: - name: MIT + name: AGPL-3.0 url: https://github.com/stirling-image/stirling-image/blob/main/LICENSE servers: @@ -158,6 +158,60 @@ paths: "401": description: Authentication required + /api/v1/tools/content-aware-resize: + post: + tags: [Tools] + summary: Content-aware resize + description: Resize an image using seam carving to intelligently remove or insert content while preserving important features. + security: + - bearerAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: Image file to process + settings: + type: string + description: | + JSON string with options: + - `width` (number, optional) — Target width in pixels + - `height` (number, optional) — Target height in pixels + - `protectFaces` (boolean, default false) — Detect and protect faces from distortion + - `blurRadius` (number 0-20, default 4) — Blur radius for energy map + - `sobelThreshold` (number 1-20, default 2) — Edge detection threshold + - `square` (boolean, default false) — Crop to square aspect ratio + + At least one of `width`, `height`, or `square` must be provided. + responses: + "200": + description: Processed image + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/ToolResponse" + - type: object + properties: + width: + type: integer + height: + type: integer + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Authentication required + /api/v1/tools/crop: post: tags: [Tools] @@ -776,6 +830,59 @@ paths: "401": description: Authentication required + /api/v1/tools/gif-tools/info: + post: + tags: [Tools] + summary: GIF info + description: Get metadata about an animated GIF including dimensions, frame count, delays, and duration. + security: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: GIF file to inspect + responses: + "200": + description: GIF metadata + content: + application/json: + schema: + type: object + properties: + width: + type: integer + height: + type: integer + pages: + type: integer + description: Number of frames + delay: + type: array + items: + type: integer + description: Per-frame delay in milliseconds + loop: + type: integer + description: Loop count (0 = infinite) + fileSize: + type: integer + duration: + type: integer + description: Total duration in milliseconds + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /api/v1/tools/smart-crop: post: tags: [Tools] @@ -1005,6 +1112,162 @@ paths: "401": description: Authentication required + /api/v1/tools/pdf-to-image: + post: + tags: [Tools] + summary: PDF to image + description: Convert PDF pages to images. Returns individual page downloads and a ZIP of all pages. + security: + - bearerAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: PDF file to convert + settings: + type: string + description: | + JSON string with options: + - `format` (string, default "png") — Output format: png, jpg, webp, avif, tiff, gif, heic, heif + - `dpi` (number 36-1200, default 150) — Resolution in dots per inch + - `quality` (number 1-100, default 85) — Output quality + - `colorMode` (string, default "color") — One of: color, grayscale, bw + - `pages` (string, default "all") — Page selection, e.g. "all", "1-3", "1,3,5" + responses: + "200": + description: Converted pages + content: + application/json: + schema: + type: object + properties: + jobId: + type: string + pageCount: + type: integer + description: Total pages in the PDF + selectedPages: + type: array + items: + type: integer + format: + type: string + pages: + type: array + items: + type: object + properties: + page: + type: integer + downloadUrl: + type: string + size: + type: integer + zipUrl: + type: string + description: URL to download all pages as ZIP + zipSize: + type: integer + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Authentication required + + /api/v1/tools/pdf-to-image/info: + post: + tags: [Tools] + summary: PDF page count + description: Get the number of pages in a PDF file. + security: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: PDF file to inspect + responses: + "200": + description: PDF info + content: + application/json: + schema: + type: object + properties: + pageCount: + type: integer + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /api/v1/tools/pdf-to-image/preview: + post: + tags: [Tools] + summary: PDF page thumbnails + description: Generate thumbnail previews for each page in a PDF (max 200 pages, JPEG 300px wide). + security: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: PDF file to preview + responses: + "200": + description: Page thumbnails + content: + application/json: + schema: + type: object + properties: + pageCount: + type: integer + thumbnails: + type: array + items: + type: object + properties: + page: + type: integer + dataUrl: + type: string + description: Base64-encoded JPEG data URL + width: + type: integer + height: + type: integer + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /api/v1/tools/split: post: tags: [Tools] @@ -1342,6 +1605,57 @@ paths: "401": description: Authentication required + /api/v1/tools/stitch: + post: + tags: [Tools] + summary: Stitch images + description: Join multiple images horizontally, vertically, or in a grid layout. + security: + - bearerAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: array + items: + type: string + format: binary + description: Two or more image files to stitch together + settings: + type: string + description: | + JSON string with options: + - `direction` (string, default "horizontal") — One of: horizontal, vertical, grid + - `gridColumns` (integer 2-10, default 2) — Columns when direction is grid + - `resizeMode` (string, default "fit") — One of: fit, original, stretch, crop + - `alignment` (string, default "center") — One of: start, center, end + - `gap` (number 0-200, default 0) — Gap between images in pixels + - `border` (number 0-50, default 0) — Border width in pixels + - `cornerRadius` (number 0-50, default 0) — Corner radius in pixels + - `backgroundColor` (string, default "#FFFFFF") — Hex color for background and gap fill + - `format` (string, default "png") — One of: png, jpeg, webp + - `quality` (number 1-100, default 90) — Output quality + responses: + "200": + description: Stitched image + content: + application/json: + schema: + $ref: "#/components/schemas/ToolResponse" + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Authentication required + /api/v1/tools/remove-background: post: tags: [Tools] @@ -1383,6 +1697,65 @@ paths: "401": description: Authentication required + /api/v1/tools/remove-background/effects: + post: + tags: [Tools] + summary: Apply background effects + description: > + Apply background replacement or effects to a previously processed remove-background result. + Requires a jobId from a prior POST /api/v1/tools/remove-background call. + security: + - bearerAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [settings] + properties: + backgroundImage: + type: string + format: binary + description: Custom background image (required when backgroundType is "image") + settings: + type: string + description: | + JSON string with options: + - `jobId` (string, required) — Job ID from the initial remove-background call + - `filename` (string, required) — Original filename + - `backgroundType` (string) — One of: transparent, color, gradient, blur, image + - `backgroundColor` (string) — Hex color for solid background + - `gradientColor1` (string) — First gradient color + - `gradientColor2` (string) — Second gradient color + - `gradientAngle` (number) — Gradient angle in degrees + - `blurEnabled` (boolean) — Enable background blur effect + - `blurIntensity` (number 0-100) — Blur strength + - `shadowEnabled` (boolean) — Enable drop shadow + - `shadowOpacity` (number 0-100) — Shadow opacity + responses: + "200": + description: Image with applied effects + content: + application/json: + schema: + type: object + properties: + jobId: + type: string + downloadUrl: + type: string + processedSize: + type: integer + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Authentication required + /api/v1/tools/upscale: post: tags: [Tools] @@ -1800,6 +2173,98 @@ paths: "401": description: Authentication required + /api/v1/tools/edit-metadata: + post: + tags: [Tools] + summary: Edit metadata + description: Edit EXIF, IPTC, GPS, and other metadata fields in an image using ExifTool. + security: + - bearerAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: Image file to process + settings: + type: string + description: | + JSON string with options: + - `artist` (string) — Artist or author name + - `copyright` (string) — Copyright notice + - `imageDescription` (string) — Image description + - `software` (string) — Software used + - `dateTime` (string) — Date/time string + - `dateTimeOriginal` (string) — Original capture date/time + - `clearGps` (boolean, default false) — Remove all GPS data + - `fieldsToRemove` (string[]) — Specific metadata fields to remove + - `gpsLatitude` (number -90 to 90) — GPS latitude + - `gpsLongitude` (number -180 to 180) — GPS longitude + - `gpsAltitude` (number) — GPS altitude in meters + - `keywords` (string[]) — Keywords or tags + - `keywordsMode` (string, default "add") — One of: add, set + - `dateShift` (string) — Shift dates by offset, e.g. "+2:00" or "-1:30" + - `setAllDates` (string) — Set all date fields to this value + - `iptcTitle` (string) — IPTC title + - `iptcHeadline` (string) — IPTC headline + - `iptcCity` (string) — IPTC city + - `iptcState` (string) — IPTC state or province + - `iptcCountry` (string) — IPTC country + responses: + "200": + description: Image with updated metadata + content: + application/json: + schema: + $ref: "#/components/schemas/ToolResponse" + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Authentication required + + /api/v1/tools/edit-metadata/inspect: + post: + tags: [Tools] + summary: Inspect metadata (ExifTool) + description: Read all metadata from an image using ExifTool. Returns all embedded EXIF, IPTC, XMP, and GPS fields. + security: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: Image file to inspect + responses: + "200": + description: Full metadata object (keys vary by image) + content: + application/json: + schema: + type: object + additionalProperties: true + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + # ─── Batch ──────────────────────────────────────────────────────────────── /api/v1/tools/{toolId}/batch: @@ -2017,6 +2482,82 @@ paths: "401": description: Authentication required + /api/v1/pipeline/tools: + get: + tags: [Pipelines] + summary: List pipeline-compatible tools + description: Returns all tool IDs that can be used as pipeline steps. + security: [] + responses: + "200": + description: Tool IDs + content: + application/json: + schema: + type: object + properties: + toolIds: + type: array + items: + type: string + + /api/v1/pipeline/batch: + post: + tags: [Pipelines] + summary: Run pipeline on multiple files + description: Execute a pipeline across multiple images. Returns a ZIP file with all processed results. + security: + - bearerAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file, pipeline] + properties: + file: + type: array + items: + type: string + format: binary + description: Image files to process + pipeline: + type: string + description: | + JSON string with pipeline definition: + - `steps` (array, required, 1-20 items) — Pipeline steps + - `steps[].toolId` (string, required) — Tool ID for this step + - `steps[].settings` (object, optional) — Tool-specific settings + clientJobId: + type: string + description: Optional client-provided job ID for progress tracking via SSE + responses: + "200": + description: ZIP file with processed images + headers: + X-Job-Id: + schema: + type: string + description: Job ID for progress tracking + X-File-Results: + schema: + type: string + description: JSON mapping of input index to output filename + content: + application/zip: + schema: + type: string + format: binary + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Authentication required + # ─── Files ──────────────────────────────────────────────────────────────── /api/v1/upload: @@ -2084,6 +2625,39 @@ paths: "404": description: File not found + /api/v1/preview: + post: + tags: [Files] + summary: Generate image preview + description: Convert any image (including HEIC/HEIF) to a WebP preview, resized to fit within 1200x1200 pixels. + security: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + description: Image file to preview + responses: + "200": + description: WebP preview image + content: + image/webp: + schema: + type: string + format: binary + "400": + description: Invalid input + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /api/v1/files: get: tags: [Files] @@ -2124,7 +2698,8 @@ paths: description: Authentication required delete: tags: [Files] - summary: Delete saved files + summary: Bulk delete saved files + description: Delete files and their entire version chains. Non-admin users can only delete their own files. security: - bearerAuth: [] requestBody: @@ -2139,9 +2714,18 @@ paths: type: array items: type: string + description: File IDs to delete (deletes entire version chain for each) responses: - "204": - description: Files deleted + "200": + description: Deletion result + content: + application/json: + schema: + type: object + properties: + deleted: + type: integer + description: Total number of records deleted across all version chains "400": description: Invalid input content: diff --git a/apps/docs/api/ai.md b/apps/docs/api/ai.md index a829562c..3e5c9a9b 100644 --- a/apps/docs/api/ai.md +++ b/apps/docs/api/ai.md @@ -12,26 +12,33 @@ The Docker image includes CUDA-accelerated ML libraries on amd64. Add `--gpus al Removes the background from an image and returns a transparent PNG. -**Model:** BiRefNet-Lite via [rembg](https://github.com/danielgatis/rembg) +**Model:** BiRefNet models via [rembg](https://github.com/danielgatis/rembg) | Parameter | Type | Description | |---|---|---| -| `model` | string | Model name. Default: `birefnet-lite`. Options include `u2net`, `isnet-general-use`, and others supported by rembg. | +| `model` | string | Model name. Default: `birefnet-general`. Available models: `birefnet-general`, `birefnet-general-lite`, `birefnet-matting`, `birefnet-portrait`, `bria-rmbg`, `u2net`. | | `alphaMatting` | boolean | Use alpha matting for finer edge detail | | `alphaMattingForegroundThreshold` | number | Foreground threshold for alpha matting (0-255) | | `alphaMattingBackgroundThreshold` | number | Background threshold for alpha matting (0-255) | +A Phase 2 effects endpoint is also available at `POST /api/v1/tools/remove-background/effects`. After removing the background, you can apply post-processing effects such as replacement backgrounds, blur, and drop shadows. + **Python script:** `packages/ai/python/remove_bg.py` ## Upscaling Increases image resolution using AI super-resolution. -**Model:** [RealESRGAN](https://github.com/xinntao/Real-ESRGAN) +**Model:** [RealESRGAN](https://github.com/xinntao/Real-ESRGAN) with Lanczos fallback | Parameter | Type | Description | |---|---|---| -| `scale` | number | Upscale factor: `2` or `4` | +| `scale` | number | Upscale factor (2-8) | +| `model` | string | Model selection: `auto`, `realesrgan`, or `lanczos`. Default: `auto`. | +| `faceEnhance` | boolean | Enable face enhancement for better facial detail | +| `denoise` | number | Denoise strength (0-1). Higher values remove more noise but may lose detail. | +| `format` | string | Output format (e.g. `png`, `webp`, `jpeg`) | +| `quality` | number | Output quality for lossy formats | Returns the upscaled image along with the original and new dimensions. @@ -39,13 +46,18 @@ Returns the upscaled image along with the original and new dimensions. ## OCR (text recognition) -Extracts text from images. +Extracts text from images. Three quality tiers are available, each trading speed for accuracy. -**Model:** [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR) +**Models:** +- `fast` - [Tesseract](https://github.com/tesseract-ocr/tesseract) for quick extraction +- `balanced` - [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR) PP-OCRv5 for general-purpose use +- `best` - PaddleOCR-VL 1.5 vision-language model for maximum accuracy | Parameter | Type | Description | |---|---|---| -| `language` | string | Language code (e.g. `en`, `ch`, `fr`, `de`) | +| `quality` | string | Quality tier: `fast`, `balanced`, or `best`. Default: `balanced`. | +| `language` | string | Language code: `auto`, `en`, `de`, `fr`, `es`, `zh`, `ja`, `ko`. Default: `auto`. | +| `enhance` | boolean | Apply image preprocessing to improve recognition accuracy | Returns structured results with text content, bounding boxes, and confidence scores for each detected text region. @@ -59,7 +71,8 @@ Detects faces in an image and applies a blur to each detected region. | Parameter | Type | Description | |---|---|---| -| `blurStrength` | number | How strongly to blur detected faces | +| `blurRadius` | number | Blur radius for detected faces (1-100). Default: `30`. | +| `sensitivity` | number | Detection sensitivity (0-1). Lower values require higher confidence. Default: `0.5`. | Returns the blurred image along with metadata about each detected face region (bounding box coordinates and confidence score). @@ -69,12 +82,41 @@ Returns the blurred image along with metadata about each detected face region (b Removes objects from images by filling in the area with generated content that matches the surroundings. -**Model:** OpenCV TELEA algorithm +**Model:** [LaMa](https://github.com/advimman/lama) (Large Mask Inpainting) via ONNX Runtime -Takes an image and a mask (white = area to erase, black = keep). Returns the inpainted image. +Takes an image and a mask file (white = area to erase, black = keep). Returns the inpainted image. GPU acceleration is available via ONNX CUDAExecutionProvider when a compatible GPU is detected. **Python script:** `packages/ai/python/inpaint.py` +## Content-aware resize (seam carving) + +Intelligently resizes images by removing or inserting seams - paths of least visual importance. This preserves the main subject and structure of the image while changing its dimensions. + +**Engine:** `caire` Go binary + +| Parameter | Type | Description | +|---|---|---| +| `width` | number | Target width in pixels | +| `height` | number | Target height in pixels | +| `protectFaces` | boolean | Use face detection to protect facial regions from seam removal | +| `blurRadius` | number | Gaussian blur radius for energy map computation (0-20) | +| `sobelThreshold` | number | Edge detection threshold for energy computation (1-20) | +| `square` | boolean | Force output to a square aspect ratio | + +## Smart crop + +Automatically crops images to focus on the most important region. Combines Sharp attention/entropy strategies with MediaPipe face detection to find the optimal crop area. + +**Models:** Sharp (attention/entropy) + [MediaPipe](https://github.com/google/mediapipe) Face Detection + +Three modes are available: + +- **subject** - Uses Sharp's attention strategy to find the most visually interesting region. +- **face** - Uses MediaPipe face detection to center the crop on detected faces. +- **trim** - Removes uniform borders and whitespace from the edges of the image. + +Parameters vary by mode. See the interactive API reference at `/api/docs` for the full parameter list for each mode. + ## How the bridge works The TypeScript bridge (`packages/ai/src/bridge.ts`) exposes a single function, `runPythonWithProgress`, that does the following for each AI call: @@ -86,6 +128,8 @@ The TypeScript bridge (`packages/ai/src/bridge.ts`) exposes a single function, ` 5. Reads the output image from the filesystem. 6. Cleans up temp files. -The persistent dispatcher pre-imports rembg, OpenCV, NumPy, and Pillow at startup. This means the first AI call after container start is fast instead of waiting for library imports. The dispatcher handles requests sequentially (Python's GIL) and reports readiness via a `{"ready": true}` message on stderr. +The persistent dispatcher pre-imports rembg, torch, PaddleOCR, MediaPipe, and the LaMa ONNX model at startup. This means the first AI call after container start is fast instead of waiting for library imports. The dispatcher handles requests sequentially (Python's GIL) and reports readiness via a `{"ready": true}` message on stderr. + +GPU detection is handled by `packages/ai/python/gpu.py`, which checks for CUDA availability at startup and configures each model to use GPU or CPU accordingly. If the Python process exits with a non-zero code, the bridge extracts a user-friendly error from stderr/stdout and throws. Timeouts default to 5 minutes. diff --git a/apps/docs/api/rest.md b/apps/docs/api/rest.md index e2be3acf..43f1c3df 100644 --- a/apps/docs/api/rest.md +++ b/apps/docs/api/rest.md @@ -3,7 +3,7 @@ The API server runs on port 1349 by default and serves all endpoints under `/api`. ::: tip Full API Reference -Your Stirling Image instance includes a complete interactive API reference at `/api/docs` (e.g. `http://your-host:1349/api/docs`) with all 67 endpoints, request/response schemas, and examples. +Your Stirling Image instance includes a complete interactive API reference at `/api/docs` (e.g. `http://your-host:1349/api/docs`) with all 80+ endpoints, request/response schemas, and examples. ::: ::: info LLM-friendly docs @@ -48,13 +48,13 @@ Send a multipart request with: | Category | Tools | |----------|-------| | **Essentials** | `resize`, `crop`, `rotate`, `convert`, `compress` | -| **Optimization** | `strip-metadata`, `bulk-rename`, `image-to-pdf`, `favicon` | -| **Adjustments** | `brightness-contrast`, `saturation`, `color-channels`, `color-effects`, `replace-color` | -| **AI** | `remove-background`, `upscale`, `erase-object`, `ocr`, `blur-faces`, `smart-crop` | +| **Optimization** | `strip-metadata`, `edit-metadata`, `bulk-rename`, `image-to-pdf`, `favicon` | +| **Adjustments** | `adjust-colors`, `replace-color` | +| **AI** | `remove-background`, `upscale`, `erase-object`, `ocr`, `blur-faces`, `smart-crop`, `content-aware-resize` | | **Watermark** | `watermark-text`, `watermark-image`, `text-overlay`, `compose` | | **Utilities** | `info`, `compare`, `find-duplicates`, `color-palette`, `qr-generate`, `barcode-read` | -| **Layout** | `collage`, `split`, `border` | -| **Format** | `svg-to-raster`, `vectorize`, `gif-tools` | +| **Layout** | `collage`, `split`, `border`, `stitch` | +| **Format** | `svg-to-raster`, `vectorize`, `gif-tools`, `pdf-to-image` | Each tool's specific settings are documented in the interactive API reference at `/api/docs` on your running instance. @@ -72,8 +72,10 @@ Chain tools into reusable workflows. ``` POST /api/v1/pipeline/execute -- Run a pipeline (multipart: file + steps JSON) +POST /api/v1/pipeline/batch -- Run a pipeline across multiple files (returns ZIP) POST /api/v1/pipeline/save -- Save a named pipeline GET /api/v1/pipeline/list -- List saved pipelines +GET /api/v1/pipeline/tools -- List all pipeline-compatible tool IDs DELETE /api/v1/pipeline/:id -- Delete a pipeline ``` diff --git a/packages/image-engine/src/engine.ts b/packages/image-engine/src/engine.ts index a2d6e578..56ca516d 100644 --- a/packages/image-engine/src/engine.ts +++ b/packages/image-engine/src/engine.ts @@ -13,6 +13,7 @@ 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 } from "./operations/sharpen.js"; import { stripMetadata } from "./operations/strip-metadata.js"; import type { BrightnessOptions, @@ -29,6 +30,7 @@ import type { RotateOptions, SaturationOptions, Sharp, + SharpenOptions, StripMetadataOptions, } from "./types.js"; import { getImageInfo } from "./utils/metadata.js"; @@ -55,6 +57,7 @@ const OPERATION_MAP: Record< "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), invert: (img) => invert(img), "edit-metadata": (img, opts) => editMetadata(img, opts as unknown as EditMetadataOptions), };