/** * `@nifrajs/image/server` - nifra's **self-hosted** image resize endpoint. `createImageHandler` returns a * `(req: Request) => Promise` you mount at the path your {@link selfHostedLoader} points to * (e.g. `"/_image"`). It validates the query, safely resolves the source (local file under a `root`, or * a remote URL on an `allowedOrigins` allowlist - both **fail-closed** against SSRF), then decodes → * resizes (never upscaling) → re-encodes via a pluggable {@link ImageBackend}. The default * {@link bunImageBackend} uses `Bun.Image` (libjpeg-turbo / libspng / libwebp, off-thread). * * This subpath touches the filesystem and a native codec, so it is for **Node/Bun servers**, not the * edge. On Workers / Vercel-Edge / Deno-Deploy there is no native codec - use the CDN `cloudflareLoader` * from `@nifrajs/image` instead. The dependency-free core (`@nifrajs/image`) never imports this module. */ import { type ImageBackend } from "./backend.js"; export * from "./backend.js"; export interface ImageHandlerOptions { /** Codec backend. Default: {@link bunImageBackend} (requires the Bun runtime). */ readonly backend?: ImageBackend; /** Absolute directory that local (path) sources resolve under. Path-traversal- and symlink-guarded. * Omit to **disable local sources** (every path source → 403). */ readonly root?: string; /** Exact origins (`https://cdn.example`) allowed for remote sources. Omitted/empty ⇒ **no remote * sources** (fail-closed against SSRF). Only `http:`/`https:` URLs are ever considered. */ readonly allowedOrigins?: readonly string[]; /** Max bytes read from a source before rejecting (413). Default 20 MiB. */ readonly maxSourceBytes?: number; /** Max source pixels (w×h) before rejecting (413) - decompression-bomb guard. Default 40 MP. */ readonly maxSourcePixels?: number; /** Hard cap on the requested width; larger `?w` is clamped down. Default 3840. */ readonly maxWidth?: number; /** Max concurrent transforms (codec work is CPU/memory-heavy). Excess requests queue. Default 4. */ readonly concurrency?: number; /** * Max requests reading a source at once - the admission width. Sized above `concurrency` on purpose: * a remote source is network-bound, and holding a codec slot across a 10s fetch would let a handful * of slow origins idle the CPU lane out entirely. Default `concurrency * 2`. * * This is also the memory bound: at most this many source buffers exist at once, so the ceiling is * `sourceConcurrency * maxSourceBytes` (default 8 x 20 MiB = 160 MiB) plus the codec's own working * set. Lower it, or `maxSourceBytes`, on a small instance. */ readonly sourceConcurrency?: number; /** Maximum queued image requests beyond the admission width. Default `concurrency * 16`. */ readonly maxQueue?: number; /** `Cache-Control: public, max-age=, immutable` seconds. Default 1 year. */ readonly cacheMaxAge?: number; /** Quality used when `?q` is absent. Default 75. */ readonly defaultQuality?: number; /** Remote-fetch timeout (ms). Default 10 000. */ readonly fetchTimeoutMs?: number; /** `fetch` implementation for remote sources (injectable for custom timeouts/proxy and for tests). * Default: the global `fetch`. */ readonly fetch?: typeof fetch; /** Require **signed URLs**: every request must carry a valid `&s=` HMAC over `(src, w, q[, exp])` or * it's rejected with `403`. Use the SAME `secret` as your `selfHostedLoader`/`signImageUrl`. This * locks the endpoint to URLs your app minted - the defense against resize-bombing. */ readonly signing?: { readonly secret: string; }; } /** * Build the resize request handler. Mount its return value at the `selfHostedLoader` endpoint: * * ```ts * const image = createImageHandler({ root: "./public", allowedOrigins: ["https://cdn.example"] }) * // inside your router: if (url.pathname === "/_image") return image(req) * ``` */ export declare function createImageHandler(options?: ImageHandlerOptions): (req: Request) => Promise; //# sourceMappingURL=server.d.ts.map