/** * SnapAPI Node.js SDK * Official client for https://snapapi.eu — EU-hosted screenshot API * * @example * ```typescript * import { SnapAPI } from 'snapapi'; * * const snap = new SnapAPI('your-api-key'); * const screenshot = await snap.capture('https://example.com'); * fs.writeFileSync('screenshot.png', screenshot); * ``` */ export interface ScreenshotOptions { /** URL to capture (required) */ url: string; /** Output format: png, jpeg, or webp (default: png) */ format?: "png" | "jpeg" | "webp"; /** Viewport width in pixels, 320-3840 (default: 1280) */ width?: number; /** Viewport height in pixels, 200-2160 (default: 800) */ height?: number; /** Capture full scrollable page (default: false) */ fullPage?: boolean; /** JPEG/WebP quality 1-100 (default: 80, ignored for PNG) */ quality?: number; /** CSS selector to wait for before capturing */ waitForSelector?: string; /** Device scale factor 1-3 (default: 1, use 2 for Retina) */ deviceScale?: number; /** Extra delay in ms after page load, 0-5000 (default: 0) */ delay?: number; /** Page load event to wait for (default: domcontentloaded) */ waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2"; } export interface SnapAPIConfig { /** API base URL (default: https://snapapi.eu) */ baseUrl?: string; /** Request timeout in ms (default: 30000) */ timeout?: number; } export class SnapAPIError extends Error { /** HTTP status code */ status: number; /** Error message from the API */ detail: string; constructor(status: number, detail: string) { super(`SnapAPI error ${status}: ${detail}`); this.name = "SnapAPIError"; this.status = status; this.detail = detail; } } export class SnapAPI { private apiKey: string; private baseUrl: string; private timeout: number; /** * Create a new SnapAPI client. * * @param apiKey - Your SnapAPI API key * @param config - Optional configuration * * @example * ```typescript * const snap = new SnapAPI('sk_live_...'); * ``` */ constructor(apiKey: string, config?: SnapAPIConfig) { if (!apiKey) throw new Error("SnapAPI: apiKey is required"); this.apiKey = apiKey; this.baseUrl = (config?.baseUrl ?? "https://snapapi.eu").replace(/\/$/, ""); this.timeout = config?.timeout ?? 30000; } /** * Capture a screenshot of a URL. * * Returns the screenshot as a Buffer (Node.js) or Uint8Array. * * @param urlOrOptions - URL string or full ScreenshotOptions object * @param options - Additional options when first arg is a URL string * @returns Screenshot image as Buffer * * @example * ```typescript * // Simple usage * const png = await snap.capture('https://example.com'); * * // With options * const jpg = await snap.capture('https://example.com', { * format: 'jpeg', * width: 1920, * height: 1080, * quality: 90 * }); * * // Full-page capture * const full = await snap.capture({ * url: 'https://example.com', * fullPage: true, * format: 'png', * deviceScale: 2 * }); * ``` * * @throws {SnapAPIError} When the API returns an error response */ async capture( urlOrOptions: string | ScreenshotOptions, options?: Omit ): Promise { const body: ScreenshotOptions = typeof urlOrOptions === "string" ? { url: urlOrOptions, ...options } : urlOrOptions; if (!body.url) throw new Error("SnapAPI: url is required"); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), this.timeout); try { const res = await fetch(`${this.baseUrl}/v1/screenshot`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify(body), signal: controller.signal, }); if (!res.ok) { let detail = `HTTP ${res.status}`; try { const json = (await res.json()) as { error?: string }; detail = json.error ?? detail; } catch {} throw new SnapAPIError(res.status, detail); } const arrayBuffer = await res.arrayBuffer(); return Buffer.from(arrayBuffer); } finally { clearTimeout(timer); } } /** * Check API health status. * * @returns Health check response * * @example * ```typescript * const health = await snap.health(); * console.log(health.status); // "ok" * ``` */ async health(): Promise<{ status: string; version: string; uptime: number; browser: { browsers: number; totalPages: number; availablePages: number; queueDepth: number; }; }> { const res = await fetch(`${this.baseUrl}/health`); if (!res.ok) throw new SnapAPIError(res.status, "Health check failed"); return res.json() as any; } } export default SnapAPI;