From 6f85b3d12acac73caf747313511c49c7bdc6c257 Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Mon, 29 Jun 2026 16:09:30 +0800 Subject: [PATCH] fix(api): support sharp 0.35 types --- apps/api/src/lib/bg-effects.ts | 4 ++-- apps/api/src/lib/output-format.ts | 4 ++-- apps/api/src/routes/tools/circle-crop.ts | 4 ++-- apps/api/src/routes/tools/collage.ts | 4 ++-- apps/api/src/routes/tools/compose.ts | 4 ++-- apps/api/src/routes/tools/convert.ts | 4 ++-- apps/api/src/routes/tools/lqip-placeholder.ts | 6 +++--- apps/api/src/routes/tools/passport-photo.ts | 4 ++-- apps/api/src/routes/tools/split.ts | 4 ++-- apps/api/src/routes/tools/sprite-sheet.ts | 4 ++-- apps/api/src/routes/tools/stitch.ts | 4 ++-- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/api/src/lib/bg-effects.ts b/apps/api/src/lib/bg-effects.ts index 8569ec14..ca9ec61b 100644 --- a/apps/api/src/lib/bg-effects.ts +++ b/apps/api/src/lib/bg-effects.ts @@ -1,4 +1,4 @@ -import sharp from "sharp"; +import sharp, { type Sharp } from "sharp"; export type BgOutputFormat = "png" | "webp" | "avif"; @@ -10,7 +10,7 @@ export const BG_FORMAT_CONTENT_TYPES: Record = { avif: "image/avif", }; -function toOutputFormat(pipeline: sharp.Sharp, format: BgOutputFormat): Promise { +function toOutputFormat(pipeline: Sharp, format: BgOutputFormat): Promise { switch (format) { case "webp": return pipeline.webp({ lossless: true }).toBuffer(); diff --git a/apps/api/src/lib/output-format.ts b/apps/api/src/lib/output-format.ts index 99286f37..45a381b0 100644 --- a/apps/api/src/lib/output-format.ts +++ b/apps/api/src/lib/output-format.ts @@ -1,6 +1,6 @@ -import sharp from "sharp"; +import sharp, { type FormatEnum } from "sharp"; -type SharpFormat = keyof sharp.FormatEnum | "avif"; +export type SharpFormat = Extract | "avif"; export interface OutputFormat { format: SharpFormat; diff --git a/apps/api/src/routes/tools/circle-crop.ts b/apps/api/src/routes/tools/circle-crop.ts index a3a8ba15..ffa6f49e 100644 --- a/apps/api/src/routes/tools/circle-crop.ts +++ b/apps/api/src/routes/tools/circle-crop.ts @@ -1,5 +1,5 @@ import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type OverlayOptions } from "sharp"; import { z } from "zod"; import { createToolRoute } from "../tool-factory.js"; @@ -69,7 +69,7 @@ export function registerCircleCrop(app: FastifyInstance) { settings.background === "transparent" ? { r: 0, g: 0, b: 0, alpha: 0 } : { ...hexToRgb(settings.background), alpha: 1 }; - const layers: sharp.OverlayOptions[] = []; + const layers: OverlayOptions[] = []; if (bw > 0) { const ring = Buffer.from( ``, diff --git a/apps/api/src/routes/tools/collage.ts b/apps/api/src/routes/tools/collage.ts index fb5ca9c1..3c442caa 100644 --- a/apps/api/src/routes/tools/collage.ts +++ b/apps/api/src/routes/tools/collage.ts @@ -1,6 +1,6 @@ import { randomUUID } from "node:crypto"; import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type OverlayOptions } from "sharp"; import { z } from "zod"; import { autoOrient } from "../../lib/auto-orient.js"; import { formatZodErrors } from "../../lib/errors.js"; @@ -563,7 +563,7 @@ export function registerCollage(app: FastifyInstance) { } const channels = bgIsTransparent ? 4 : 3; - const composites: sharp.OverlayOptions[] = []; + const composites: OverlayOptions[] = []; const cellSettings = settings.cells ?? []; for (let i = 0; i < cellRects.length && i < files.length; i++) { diff --git a/apps/api/src/routes/tools/compose.ts b/apps/api/src/routes/tools/compose.ts index 10521985..c0321dfa 100644 --- a/apps/api/src/routes/tools/compose.ts +++ b/apps/api/src/routes/tools/compose.ts @@ -1,6 +1,6 @@ import { randomUUID } from "node:crypto"; import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type Blend } from "sharp"; import { z } from "zod"; import { autoOrient } from "../../lib/auto-orient.js"; import { formatZodErrors } from "../../lib/errors.js"; @@ -142,7 +142,7 @@ export function registerCompose(app: FastifyInstance) { input: processedOverlay, top: settings.y, left: settings.x, - blend: settings.blendMode as sharp.Blend, + blend: settings.blendMode as Blend, }, ]) .toBuffer(); diff --git a/apps/api/src/routes/tools/convert.ts b/apps/api/src/routes/tools/convert.ts index a133d852..591d21ca 100644 --- a/apps/api/src/routes/tools/convert.ts +++ b/apps/api/src/routes/tools/convert.ts @@ -6,7 +6,7 @@ import { extname, join } from "node:path"; import { promisify } from "node:util"; import { convert } from "@snapotter/image-engine"; import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type SharpOptions } from "sharp"; import { z } from "zod"; import { encodeBmp, @@ -120,7 +120,7 @@ export function registerConvert(app: FastifyInstance) { } const inputExt = extname(filename).toLowerCase().replace(".", ""); - const sharpOpts: sharp.SharpOptions = isSvgBuffer(inputBuffer) ? { density: 300 } : {}; + const sharpOpts: SharpOptions = isSvgBuffer(inputBuffer) ? { density: 300 } : {}; // Preserve animation frames when both input and output are animatable formats if (ANIMATABLE_FORMATS.has(inputExt) && ANIMATABLE_FORMATS.has(settings.format)) { sharpOpts.animated = true; diff --git a/apps/api/src/routes/tools/lqip-placeholder.ts b/apps/api/src/routes/tools/lqip-placeholder.ts index 12d28b4f..d6e84c45 100644 --- a/apps/api/src/routes/tools/lqip-placeholder.ts +++ b/apps/api/src/routes/tools/lqip-placeholder.ts @@ -1,5 +1,5 @@ import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type Sharp } from "sharp"; import { z } from "zod"; import { createToolRoute } from "../tool-factory.js"; @@ -23,7 +23,7 @@ const EXT: Record = { jpeg: ".jpg", }; -function encode(pipeline: sharp.Sharp, fmt: string, q: number): sharp.Sharp { +function encode(pipeline: Sharp, fmt: string, q: number): Sharp { if (fmt === "jpeg") return pipeline.jpeg({ quality: q }); if (fmt === "png") return pipeline.png(); return pipeline.webp({ quality: q }); @@ -41,7 +41,7 @@ export function registerLqipPlaceholder(app: FastifyInstance) { const inputBuffer = ctx.inputs[0].buffer; const filename = ctx.inputs[0].filename; - let pipeline: sharp.Sharp; + let pipeline: Sharp; if (settings.strategy === "solid") { const pixel = await sharp(inputBuffer).resize(1, 1).raw().toBuffer(); diff --git a/apps/api/src/routes/tools/passport-photo.ts b/apps/api/src/routes/tools/passport-photo.ts index dd2482e0..d2c17739 100644 --- a/apps/api/src/routes/tools/passport-photo.ts +++ b/apps/api/src/routes/tools/passport-photo.ts @@ -5,7 +5,7 @@ import { join } from "node:path"; import { detectFaceLandmarks, removeBackground } from "@snapotter/ai"; import { FEATURE_BUNDLES, PASSPORT_SPECS, PRINT_LAYOUTS } from "@snapotter/shared"; import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; -import sharp from "sharp"; +import sharp, { type OverlayOptions } from "sharp"; import { z } from "zod"; import { autoOrient } from "../../lib/auto-orient.js"; import { formatZodErrors } from "../../lib/errors.js"; @@ -90,7 +90,7 @@ async function generatePrintSheet( .toBuffer(); // Build composite inputs - const composites: sharp.OverlayOptions[] = []; + const composites: OverlayOptions[] = []; for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { composites.push({ diff --git a/apps/api/src/routes/tools/split.ts b/apps/api/src/routes/tools/split.ts index 32a32028..85d7ab22 100644 --- a/apps/api/src/routes/tools/split.ts +++ b/apps/api/src/routes/tools/split.ts @@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto"; import { extname } from "node:path"; import archiver from "archiver"; import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type FormatEnum } from "sharp"; import { z } from "zod"; import { autoOrient } from "../../lib/auto-orient.js"; import { getSecurityHeaders } from "../../lib/csp.js"; @@ -24,7 +24,7 @@ const settingsSchema = z.object({ quality: z.number().min(1).max(100).default(90), }); -type SharpFormat = keyof sharp.FormatEnum | "avif"; +type SharpFormat = Extract | "avif"; function resolveOutputFormat( outputFormat: string, diff --git a/apps/api/src/routes/tools/sprite-sheet.ts b/apps/api/src/routes/tools/sprite-sheet.ts index aaa91d2a..a98eda86 100644 --- a/apps/api/src/routes/tools/sprite-sheet.ts +++ b/apps/api/src/routes/tools/sprite-sheet.ts @@ -1,5 +1,5 @@ import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type OverlayOptions } from "sharp"; import { z } from "zod"; import { InputValidationError } from "../../modality/contract.js"; import { createToolRoute } from "../tool-factory.js"; @@ -51,7 +51,7 @@ export function registerSpriteSheet(app: FastifyInstance) { const canvasH = rows * cellH + (rows - 1) * pad; const bg = parseHex(settings.background); - const composites: sharp.OverlayOptions[] = []; + const composites: OverlayOptions[] = []; const frames: Array<{ index: number; left: number; diff --git a/apps/api/src/routes/tools/stitch.ts b/apps/api/src/routes/tools/stitch.ts index b415d945..87be3ffd 100644 --- a/apps/api/src/routes/tools/stitch.ts +++ b/apps/api/src/routes/tools/stitch.ts @@ -1,6 +1,6 @@ import { randomUUID } from "node:crypto"; import type { FastifyInstance } from "fastify"; -import sharp from "sharp"; +import sharp, { type OverlayOptions } from "sharp"; import { z } from "zod"; import { env } from "../../config.js"; import { autoOrient } from "../../lib/auto-orient.js"; @@ -168,7 +168,7 @@ export function registerStitch(app: FastifyInstance) { let canvasWidth: number; let canvasHeight: number; - const composites: sharp.OverlayOptions[] = []; + const composites: OverlayOptions[] = []; if (isGrid) { const cols = Math.min(settings.gridColumns, prepared.length);