@useknockout/node 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/index.cjs +69 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -1
- package/dist/index.d.ts +67 -1
- package/dist/index.js +69 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
# 🥊 @useknockout/node
|
|
4
|
+
[](https://huggingface.co/spaces/tlorents/useknockout-demo)
|
|
4
5
|
|
|
5
6
|
**Official TypeScript / Node.js client for [useknockout](https://github.com/useknockout/api) — state-of-the-art background removal API.**
|
|
6
7
|
|
|
@@ -272,8 +273,14 @@ E-commerce preset: cutout + centered on canvas + optional drop shadow + standard
|
|
|
272
273
|
| `aspect` | `string` | `"W:H"` format, e.g. `"1:1"`, `"4:5"`, `"16:9"`. Default `"1:1"`. |
|
|
273
274
|
| `padding` | `number` | Padding in pixels. Default `48`. |
|
|
274
275
|
| `shadow` | `boolean` | Include drop shadow. Default `true`. |
|
|
276
|
+
| `transparent` | `boolean` | Keep a transparent background. Ignores `bgColor` and `shadow`; output is PNG (jpg is coerced). Default `false`. |
|
|
275
277
|
| `format` | `"png" \| "webp" \| "jpg"` | Default `"jpg"`. |
|
|
276
278
|
|
|
279
|
+
```ts
|
|
280
|
+
// Transparent product cutout, centered & squared
|
|
281
|
+
const cutout = await client.studioShot({ file: "./product.jpg", transparent: true });
|
|
282
|
+
```
|
|
283
|
+
|
|
277
284
|
### `client.compare(input)`
|
|
278
285
|
|
|
279
286
|
Before/after side-by-side preview — original on the left, transparent cutout (on a checkerboard) on the right.
|
|
@@ -352,6 +359,56 @@ const restored = await client.faceRestore({ file: "./blurry-portrait.jpg" });
|
|
|
352
359
|
| `file` | `FileInput` | Image with one or more faces. |
|
|
353
360
|
| `format` | `"png" \| "webp" \| "jpg"` | Default `"png"`. |
|
|
354
361
|
|
|
362
|
+
### `client.colorize(input)` — v0.7.0
|
|
363
|
+
|
|
364
|
+
**DDColor colorization.** Turns black-and-white or faded photos into natural color. Single call, no params required.
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
const colored = await client.colorize({ file: "./old-photo.jpg" });
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
| Field | Type | Description |
|
|
371
|
+
|---|---|---|
|
|
372
|
+
| `file` | `FileInput` | Grayscale or faded source image. |
|
|
373
|
+
| `format` | `"png" \| "webp" \| "jpg"` | Default `"png"`. |
|
|
374
|
+
|
|
375
|
+
### `client.silhouette(input)` — v0.7.1
|
|
376
|
+
|
|
377
|
+
**Two-tone silhouette portrait.** Runs BiRefNet, then flattens subject and background into two solid colors. Defaults to a purple subject on white.
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
const sil = await client.silhouette({ file: "./portrait.jpg" });
|
|
381
|
+
const custom = await client.silhouette({ file: "./portrait.jpg", subjectColor: "#000000", bgColor: "#F5F5F5" });
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
| Field | Type | Description |
|
|
385
|
+
|---|---|---|
|
|
386
|
+
| `file` | `FileInput` | Source image. |
|
|
387
|
+
| `subjectColor` | `string` | Hex color for the subject. Default `"#7C3AED"`. |
|
|
388
|
+
| `bgColor` | `string` | Hex color for the background. Default `"#FFFFFF"`. |
|
|
389
|
+
| `format` | `"png" \| "webp" \| "jpg"` | Default `"png"`. |
|
|
390
|
+
|
|
391
|
+
### `client.inpaint(input)` — v0.8.0
|
|
392
|
+
|
|
393
|
+
**LaMa large-mask inpainting (Apache-2.0).** Removes objects or fills regions. Three modes: **auto-subject** (no `mask`/`bbox` — runs BiRefNet and inverts the subject), **mask** (white pixels = inpaint), or **bbox** (rectangular region).
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
// auto-subject: erase the main subject
|
|
397
|
+
const clean = await client.inpaint({ file: "./photo.jpg" });
|
|
398
|
+
// mask mode
|
|
399
|
+
const masked = await client.inpaint({ file: "./photo.jpg", mask: "./mask.png" });
|
|
400
|
+
// bbox mode
|
|
401
|
+
const region = await client.inpaint({ file: "./photo.jpg", bbox: { x: 100, y: 100, w: 300, h: 400 } });
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
| Field | Type | Description |
|
|
405
|
+
|---|---|---|
|
|
406
|
+
| `file` | `FileInput` | Source image. |
|
|
407
|
+
| `mask` | `FileInput` | Optional. White pixels = inpaint, black = keep. Mutually exclusive with `bbox`. |
|
|
408
|
+
| `bbox` | `{ x, y, w, h }` | Optional rectangular region to inpaint. Mutually exclusive with `mask`. |
|
|
409
|
+
| `dilation` | `number` | Mask dilation in pixels. Default `8`, range `0..32`. |
|
|
410
|
+
| `format` | `"png" \| "webp" \| "jpg"` | Default `"png"`. |
|
|
411
|
+
|
|
355
412
|
### `client.health()`
|
|
356
413
|
|
|
357
414
|
Returns: `Promise<{ status: string; model: string }>`. No auth required.
|
package/dist/index.cjs
CHANGED
|
@@ -7,7 +7,7 @@ var path = require('path');
|
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
var DEFAULT_BASE_URL = "https://useknockout--api.modal.run";
|
|
10
|
-
var SDK_VERSION = "0.
|
|
10
|
+
var SDK_VERSION = "0.3.0";
|
|
11
11
|
var KnockoutError = class _KnockoutError extends Error {
|
|
12
12
|
status;
|
|
13
13
|
code;
|
|
@@ -262,6 +262,7 @@ var Knockout = class {
|
|
|
262
262
|
if (input.aspect) form.append("aspect", input.aspect);
|
|
263
263
|
if (input.padding !== void 0) form.append("padding", String(input.padding));
|
|
264
264
|
if (input.shadow !== void 0) form.append("shadow", input.shadow ? "true" : "false");
|
|
265
|
+
if (input.transparent !== void 0) form.append("transparent", input.transparent ? "true" : "false");
|
|
265
266
|
form.append("format", format);
|
|
266
267
|
const res = await this.request("POST", "/studio-shot", { body: form });
|
|
267
268
|
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
@@ -383,6 +384,73 @@ var Knockout = class {
|
|
|
383
384
|
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
384
385
|
return Buffer.from(await res.arrayBuffer());
|
|
385
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Inpaint a region of an image using LaMa (Apache-2.0). Three modes,
|
|
389
|
+
* auto-detected from what you pass:
|
|
390
|
+
*
|
|
391
|
+
* 1. **auto-subject** — pass only `file`. BiRefNet derives the subject
|
|
392
|
+
* mask, inverts it, and LaMa fills the subject region with plausible
|
|
393
|
+
* background. Drop in a photo, get the subject erased.
|
|
394
|
+
* 2. **mask** — pass `file` + `mask` (any image, white = inpaint).
|
|
395
|
+
* 3. **bbox** — pass `file` + `bbox: { x, y, w, h }`. Rectangular region.
|
|
396
|
+
*
|
|
397
|
+
* `dilation` (0..32, default 8) expands the mask before LaMa runs to
|
|
398
|
+
* reduce ghost outlines from tight masks.
|
|
399
|
+
*
|
|
400
|
+
* @example Auto-erase subject
|
|
401
|
+
* const png = await client.inpaint({ file: "./photo.jpg" });
|
|
402
|
+
*
|
|
403
|
+
* @example Erase a rectangular region
|
|
404
|
+
* const png = await client.inpaint({ file: "./photo.jpg", bbox: { x: 100, y: 100, w: 300, h: 400 } });
|
|
405
|
+
*/
|
|
406
|
+
async inpaint(input) {
|
|
407
|
+
const format = input.format ?? "png";
|
|
408
|
+
const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });
|
|
409
|
+
const form = new FormData();
|
|
410
|
+
form.append("file", blob, filename);
|
|
411
|
+
if (input.mask !== void 0) {
|
|
412
|
+
const { blob: maskBlob, filename: maskFilename } = await toBlob({
|
|
413
|
+
file: input.mask,
|
|
414
|
+
filename: input.maskFilename ?? "mask.png"
|
|
415
|
+
});
|
|
416
|
+
form.append("mask", maskBlob, maskFilename);
|
|
417
|
+
}
|
|
418
|
+
if (input.bbox) {
|
|
419
|
+
form.append("x", String(input.bbox.x));
|
|
420
|
+
form.append("y", String(input.bbox.y));
|
|
421
|
+
form.append("w", String(input.bbox.w));
|
|
422
|
+
form.append("h", String(input.bbox.h));
|
|
423
|
+
}
|
|
424
|
+
if (input.dilation !== void 0) form.append("dilation", String(input.dilation));
|
|
425
|
+
form.append("format", format);
|
|
426
|
+
const res = await this.request("POST", "/inpaint", { body: form });
|
|
427
|
+
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
428
|
+
return Buffer.from(await res.arrayBuffer());
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Two-tone silhouette portrait — subject in one solid color, bg in another.
|
|
432
|
+
* Apple Music / Spotify avatar aesthetic. Reuses BiRefNet mask path; no new
|
|
433
|
+
* model load, fast.
|
|
434
|
+
*
|
|
435
|
+
* @example
|
|
436
|
+
* const png = await client.silhouette({
|
|
437
|
+
* file: "./portrait.jpg",
|
|
438
|
+
* subjectColor: "#1E2960",
|
|
439
|
+
* bgColor: "#F0857C",
|
|
440
|
+
* });
|
|
441
|
+
*/
|
|
442
|
+
async silhouette(input) {
|
|
443
|
+
const format = input.format ?? "png";
|
|
444
|
+
const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });
|
|
445
|
+
const form = new FormData();
|
|
446
|
+
form.append("file", blob, filename);
|
|
447
|
+
if (input.subjectColor) form.append("subject_color", input.subjectColor);
|
|
448
|
+
if (input.bgColor) form.append("bg_color", input.bgColor);
|
|
449
|
+
form.append("format", format);
|
|
450
|
+
const res = await this.request("POST", "/silhouette", { body: form });
|
|
451
|
+
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
452
|
+
return Buffer.from(await res.arrayBuffer());
|
|
453
|
+
}
|
|
386
454
|
/**
|
|
387
455
|
* Remove the background from up to 10 remote image URLs in a single call.
|
|
388
456
|
*
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["readFile","basename"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @useknockout/node — official TypeScript / Node.js client for the useknockout API.\r\n *\r\n * Quick start:\r\n *\r\n * import { Knockout } from \"@useknockout/node\";\r\n *\r\n * const client = new Knockout({ token: process.env.KNOCKOUT_TOKEN! });\r\n * const png = await client.remove({ file: \"./input.jpg\" }); // Buffer of PNG bytes\r\n * await writeFile(\"out.png\", png);\r\n */\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://useknockout--api.modal.run\";\r\nconst SDK_VERSION = \"0.1.0\";\r\n\r\nexport type OutputFormat = \"png\" | \"webp\";\r\nexport type OpaqueFormat = \"png\" | \"webp\" | \"jpg\";\r\n\r\ntype FileInput = string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n\r\nexport interface KnockoutOptions {\r\n /** API bearer token. Required unless your self-hosted instance has no auth. */\r\n token?: string;\r\n /** Override the API base URL. Defaults to the hosted endpoint. */\r\n baseUrl?: string;\r\n /** Per-request timeout in milliseconds. Default 60_000. */\r\n timeoutMs?: number;\r\n /** Custom fetch (useful for edge runtimes / polyfills). Defaults to global fetch. */\r\n fetch?: typeof fetch;\r\n}\r\n\r\nexport interface RemoveInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface RemoveUrlInput {\r\n /** Remote URL of the image to process. */\r\n url: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface ReplaceBgInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the foreground image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Hex color for the new background. Default \"#FFFFFF\". Ignored if `bgUrl` is set. */\r\n bgColor?: string;\r\n /** Remote URL of an image to use as the new background. Takes precedence over `bgColor`. */\r\n bgUrl?: string;\r\n /** Output format. \"jpg\" is smallest. Default \"png\". */\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface BatchInput {\r\n /** Array of local paths / buffers / blobs. Up to 10. */\r\n files: Array<string | Buffer | Blob | ArrayBuffer | Uint8Array>;\r\n /** Optional filenames aligned to `files`. */\r\n filenames?: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchUrlInput {\r\n /** Remote URLs to process. Up to 10. */\r\n urls: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchResultItem {\r\n filename?: string;\r\n url?: string;\r\n success: boolean;\r\n format?: OutputFormat;\r\n size_bytes?: number;\r\n data_base64?: string;\r\n error?: string;\r\n}\r\n\r\nexport interface BatchResponse {\r\n count: number;\r\n format: OutputFormat;\r\n results: BatchResultItem[];\r\n}\r\n\r\nexport interface MaskInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface SmartCropInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Padding around the subject bbox, in pixels. Default 24. */\r\n padding?: number;\r\n /** Return transparent cutout (true) or cropped region from original (false). Default true. */\r\n transparent?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ShadowInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n bgUrl?: string;\r\n shadowColor?: string;\r\n shadowOffsetX?: number;\r\n shadowOffsetY?: number;\r\n shadowBlur?: number;\r\n shadowOpacity?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface StickerInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#FFFFFF\". */\r\n strokeColor?: string;\r\n /** Outline width in pixels. Default 20. */\r\n strokeWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface OutlineInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#000000\". */\r\n outlineColor?: string;\r\n /** Outline width in pixels. Default 4. */\r\n outlineWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface StudioShotInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n /** e.g. \"1:1\", \"4:5\", \"16:9\". Default \"1:1\". */\r\n aspect?: string;\r\n padding?: number;\r\n shadow?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface CompareInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface HeadshotInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the background. Default \"#FFFFFF\". Ignored if `bgBlur` is true. */\r\n bgColor?: string;\r\n /** Use a blurred copy of the original image as the background. Default false. */\r\n bgBlur?: boolean;\r\n /** Gaussian blur radius for the background when `bgBlur` is true. Default 20. */\r\n blurRadius?: number;\r\n /** Output aspect \"W:H\". Default \"4:5\" (portrait). */\r\n aspect?: string;\r\n /** Padding around the subject bbox, in pixels. Default 64. */\r\n padding?: number;\r\n /** Vertical headroom as a ratio of canvas height (0–0.5). Default 0.18. */\r\n headTopRatio?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface PreviewInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Long-edge cap in pixels (64–1024). Default 512. */\r\n maxDim?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface UpscaleInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Upscale factor: 2 or 4. Default 4. */\r\n scale?: 2 | 4;\r\n /**\r\n * Backend. `swin2sr` (default, v0.6.0+) is sharper on real photos.\r\n * `realesrgan` is the legacy backend — better on anime/illustrations.\r\n */\r\n model?: \"swin2sr\" | \"realesrgan\";\r\n /** Route through GFPGAN to fix facial detail. Slower, use for portraits. Implies realesrgan. */\r\n faceEnhance?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface FaceRestoreInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Restore only the most prominent face (faster). Default false (all faces). */\r\n onlyCenterFace?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ColorizeInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface EstimateInput {\r\n /** Endpoint name without leading slash, e.g. \"remove\" or \"headshot\". */\r\n endpoint: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface EstimateResponse {\r\n endpoint: string;\r\n image_pixels: number;\r\n est_latency_ms_warm: number;\r\n est_latency_ms_cold: number;\r\n est_cost_usd: number;\r\n free_during_beta: boolean;\r\n note: string;\r\n}\r\n\r\nexport interface StatsDay {\r\n date: string;\r\n count: number;\r\n}\r\n\r\nexport interface StatsResponse {\r\n total_processed: number;\r\n today: number;\r\n last_7_days: StatsDay[];\r\n error?: string;\r\n detail?: string;\r\n}\r\n\r\nexport interface HealthResponse {\r\n status: string;\r\n model: string;\r\n}\r\n\r\n/**\r\n * Error thrown when the API returns a non-2xx response.\r\n */\r\nexport class KnockoutError extends Error {\r\n public readonly status: number;\r\n public readonly code: \"auth\" | \"rate_limit\" | \"bad_request\" | \"payload_too_large\" | \"server\" | \"unknown\";\r\n public readonly body: string;\r\n\r\n constructor(status: number, body: string) {\r\n const code = KnockoutError.classify(status);\r\n super(`Knockout API error ${status} (${code}): ${body || \"no body\"}`);\r\n this.name = \"KnockoutError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n }\r\n\r\n private static classify(status: number): KnockoutError[\"code\"] {\r\n if (status === 401 || status === 403) return \"auth\";\r\n if (status === 429) return \"rate_limit\";\r\n if (status === 413) return \"payload_too_large\";\r\n if (status >= 400 && status < 500) return \"bad_request\";\r\n if (status >= 500) return \"server\";\r\n return \"unknown\";\r\n }\r\n}\r\n\r\n/**\r\n * useknockout API client.\r\n *\r\n * All methods return a `Buffer` (Node) of the processed image bytes.\r\n * Use `.toString(\"base64\")` or `writeFile(path, buf)` to persist.\r\n */\r\nexport class Knockout {\r\n private readonly baseUrl: string;\r\n private readonly token: string | undefined;\r\n private readonly timeoutMs: number;\r\n private readonly fetchImpl: typeof fetch;\r\n\r\n constructor(options: KnockoutOptions = {}) {\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\r\n this.token = options.token;\r\n this.timeoutMs = options.timeoutMs ?? 60_000;\r\n const fetchRef = options.fetch ?? globalThis.fetch;\r\n if (!fetchRef) {\r\n throw new Error(\r\n \"Global fetch is unavailable. Provide `options.fetch` or use Node 18+.\"\r\n );\r\n }\r\n this.fetchImpl = fetchRef.bind(globalThis);\r\n }\r\n\r\n /** Hit GET /health — no auth required. */\r\n async health(): Promise<HealthResponse> {\r\n const res = await this.request(\"GET\", \"/health\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as HealthResponse;\r\n }\r\n\r\n /**\r\n * Public usage counter — total images processed all-time, today, and a 7-day breakdown.\r\n * Use for landing-page social proof. Eventually consistent across containers.\r\n */\r\n async stats(): Promise<StatsResponse> {\r\n const res = await this.request(\"GET\", \"/stats\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as StatsResponse;\r\n }\r\n\r\n /**\r\n * Predict latency + cost for an endpoint and image size without doing any GPU work.\r\n *\r\n * @example\r\n * const est = await client.estimate({ endpoint: \"remove\", width: 1024, height: 1024 });\r\n */\r\n async estimate(input: EstimateInput): Promise<EstimateResponse> {\r\n const res = await this.request(\"POST\", \"/estimate\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n endpoint: input.endpoint,\r\n width: input.width,\r\n height: input.height,\r\n }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as EstimateResponse;\r\n }\r\n\r\n /**\r\n * Remove the background from an image, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.remove({ file: \"./input.jpg\" });\r\n */\r\n async remove(input: RemoveInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob(input);\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const res = await this.request(\"POST\", `/remove?format=${format}`, {\r\n body: form,\r\n });\r\n\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from a remote URL, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.removeUrl({ url: \"https://example.com/cat.jpg\" });\r\n */\r\n async removeUrl(input: RemoveUrlInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const res = await this.request(\"POST\", \"/remove-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ url: input.url, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Replace the background with a solid color or a remote image.\r\n *\r\n * @example Solid color\r\n * const jpg = await client.replaceBackground({ file: \"./cat.jpg\", bgColor: \"#FF5733\", format: \"jpg\" });\r\n *\r\n * @example Remote image as new background\r\n * const png = await client.replaceBackground({\r\n * file: \"./cat.jpg\",\r\n * bgUrl: \"https://example.com/beach.jpg\",\r\n * });\r\n */\r\n async replaceBackground(input: ReplaceBgInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const params = new URLSearchParams({ format });\r\n if (input.bgUrl) params.set(\"bg_url\", input.bgUrl);\r\n if (input.bgColor) params.set(\"bg_color\", input.bgColor);\r\n\r\n const res = await this.request(\"POST\", `/replace-bg?${params.toString()}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 images in a single call.\r\n * Returns a JSON object with base64-encoded result bytes per image.\r\n *\r\n * @example\r\n * const batch = await client.removeBatch({\r\n * files: [\"./a.jpg\", \"./b.jpg\", \"./c.jpg\"],\r\n * format: \"png\",\r\n * });\r\n * for (const r of batch.results) {\r\n * if (r.success) await writeFile(r.filename!, Buffer.from(r.data_base64!, \"base64\"));\r\n * }\r\n */\r\n async removeBatch(input: BatchInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.files.length === 0) throw new Error(\"At least one file required\");\r\n if (input.files.length > 10) throw new Error(\"Max 10 files per batch\");\r\n\r\n const form = new FormData();\r\n for (let i = 0; i < input.files.length; i++) {\r\n const name = input.filenames?.[i];\r\n const { blob, filename } = await toBlob({ file: input.files[i]!, filename: name });\r\n form.append(\"files\", blob, filename);\r\n }\r\n\r\n const res = await this.request(\"POST\", `/remove-batch?format=${format}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n /**\r\n * Return only the alpha mask as a grayscale PNG/WebP.\r\n * Useful when chaining into your own compositing pipeline.\r\n */\r\n async mask(input: MaskInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/mask\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Auto-crop to the subject's tight bounding box with configurable padding.\r\n * Returns either a transparent cutout or a cropped region from the original image.\r\n */\r\n async smartCrop(input: SmartCropInput): Promise<Buffer> {\r\n const transparent = input.transparent ?? true;\r\n const format: OpaqueFormat =\r\n (input.format as OpaqueFormat | undefined) ?? (transparent ? \"png\" : \"jpg\");\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"padding\", String(input.padding ?? 24));\r\n form.append(\"transparent\", transparent ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/smart-crop\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Composite subject onto a new background with a configurable drop shadow.\r\n */\r\n async shadow(input: ShadowInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgUrl) form.append(\"bg_url\", input.bgUrl);\r\n if (input.shadowColor) form.append(\"shadow_color\", input.shadowColor);\r\n if (input.shadowOffsetX !== undefined) form.append(\"shadow_offset_x\", String(input.shadowOffsetX));\r\n if (input.shadowOffsetY !== undefined) form.append(\"shadow_offset_y\", String(input.shadowOffsetY));\r\n if (input.shadowBlur !== undefined) form.append(\"shadow_blur\", String(input.shadowBlur));\r\n if (input.shadowOpacity !== undefined) form.append(\"shadow_opacity\", String(input.shadowOpacity));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/shadow\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Sticker style — subject with a thick outline on transparent background.\r\n * Perfect for WhatsApp / iMessage / Telegram stickers.\r\n */\r\n async sticker(input: StickerInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.strokeColor) form.append(\"stroke_color\", input.strokeColor);\r\n if (input.strokeWidth !== undefined) form.append(\"stroke_width\", String(input.strokeWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/sticker\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Subject on transparent background with a thin configurable outline.\r\n */\r\n async outline(input: OutlineInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.outlineColor) form.append(\"outline_color\", input.outlineColor);\r\n if (input.outlineWidth !== undefined) form.append(\"outline_width\", String(input.outlineWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/outline\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * E-commerce preset — cutout + tight crop + centered + optional shadow on a standard aspect canvas.\r\n */\r\n async studioShot(input: StudioShotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.shadow !== undefined) form.append(\"shadow\", input.shadow ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/studio-shot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Before/after side-by-side preview — original on left, transparent cutout (on a checkerboard) on right.\r\n */\r\n async compare(input: CompareInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/compare\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * LinkedIn-ready headshot preset — bg removal + portrait crop + center face + bg color or blur.\r\n *\r\n * @example Solid bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgColor: \"#0A0A0A\" });\r\n *\r\n * @example Blurred original as bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgBlur: true, blurRadius: 24 });\r\n */\r\n async headshot(input: HeadshotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgBlur !== undefined) form.append(\"bg_blur\", input.bgBlur ? \"true\" : \"false\");\r\n if (input.blurRadius !== undefined) form.append(\"blur_radius\", String(input.blurRadius));\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.headTopRatio !== undefined) form.append(\"head_top_ratio\", String(input.headTopRatio));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/headshot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Fast low-res preview cutout (~80ms warm). Skips pymatting refinement and downscales\r\n * input to `maxDim` (default 512px on the long edge). Use for UX progress indicators.\r\n */\r\n async preview(input: PreviewInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"max_dim\", String(input.maxDim ?? 512));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/preview\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * 2x / 4x super-resolution. Defaults to Swin2SR (SwinV2 transformer) for sharper\r\n * detail on real photos. Pass `model: \"realesrgan\"` for the legacy backend\r\n * (better on anime / illustrations). `faceEnhance: true` routes portraits\r\n * through GFPGAN.\r\n *\r\n * @example Cutout → 4x upscale (print-ready)\r\n * const png = await client.remove({ file: \"./photo.jpg\" });\r\n * const big = await client.upscale({ file: png, scale: 4 });\r\n */\r\n async upscale(input: UpscaleInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"scale\", String(input.scale ?? 4));\r\n form.append(\"model\", input.model ?? \"swin2sr\");\r\n if (input.faceEnhance !== undefined) {\r\n form.append(\"face_enhance\", input.faceEnhance ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/upscale\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * GFPGAN v1.4 portrait restoration — fix blurry / damaged / low-res faces.\r\n * Background is upscaled 2x by Real-ESRGAN. Pairs with /headshot.\r\n */\r\n async faceRestore(input: FaceRestoreInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.onlyCenterFace !== undefined) {\r\n form.append(\"only_center_face\", input.onlyCenterFace ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/face-restore\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Colorize a black-and-white or grayscale photo (DDColor v0.7.0+).\r\n *\r\n * Apache-2.0 model, single feed-forward (no diffusion), ~500ms warm on L4.\r\n * Works on any input — color photos are converted to grayscale internally\r\n * before color is predicted, which makes round-trip recoloring easy too.\r\n *\r\n * @example\r\n * const buf = await client.colorize({ file: \"./old-photo.jpg\" });\r\n * await writeFile(\"colorized.png\", buf);\r\n */\r\n async colorize(input: ColorizeInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/colorize\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 remote image URLs in a single call.\r\n *\r\n * @example\r\n * const batch = await client.removeBatchUrl({\r\n * urls: [\"https://a.jpg\", \"https://b.jpg\"],\r\n * });\r\n */\r\n async removeBatchUrl(input: BatchUrlInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.urls.length === 0) throw new Error(\"At least one URL required\");\r\n if (input.urls.length > 10) throw new Error(\"Max 10 URLs per batch\");\r\n\r\n const res = await this.request(\"POST\", \"/remove-batch-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ urls: input.urls, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n private async request(\r\n method: \"GET\" | \"POST\",\r\n path: string,\r\n init: { headers?: Record<string, string>; body?: BodyInit } = {}\r\n ): Promise<Response> {\r\n const url = `${this.baseUrl}${path}`;\r\n const headers: Record<string, string> = {\r\n \"User-Agent\": `useknockout-node/${SDK_VERSION}`,\r\n ...(init.headers ?? {}),\r\n };\r\n if (this.token) headers[\"Authorization\"] = `Bearer ${this.token}`;\r\n\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n try {\r\n return await this.fetchImpl(url, {\r\n method,\r\n headers,\r\n body: init.body,\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n\r\nasync function toBlob(\r\n input: { file: string | Buffer | Blob | ArrayBuffer | Uint8Array; filename?: string }\r\n): Promise<{ blob: Blob; filename: string }> {\r\n const { file } = input;\r\n const filename = input.filename ?? inferFilename(file);\r\n\r\n if (typeof file === \"string\") {\r\n const data = await readFile(file);\r\n return { blob: new Blob([new Uint8Array(data)]), filename };\r\n }\r\n if (file instanceof Blob) {\r\n return { blob: file, filename };\r\n }\r\n if (file instanceof ArrayBuffer) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (file instanceof Uint8Array) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (Buffer.isBuffer(file)) {\r\n return {\r\n blob: new Blob([new Uint8Array(file)]),\r\n filename,\r\n };\r\n }\r\n throw new TypeError(\"Unsupported `file` input. Provide a path, Buffer, Blob, ArrayBuffer, or Uint8Array.\");\r\n}\r\n\r\nfunction inferFilename(file: string | Buffer | Blob | ArrayBuffer | Uint8Array): string {\r\n if (typeof file === \"string\") return basename(file) || \"image\";\r\n return \"image\";\r\n}\r\n\r\nexport default Knockout;\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["readFile","basename"],"mappings":";;;;;;;;AAeO,IAAM,gBAAA,GAAmB;AAChC,IAAM,WAAA,GAAc,OAAA;AAkRb,IAAM,aAAA,GAAN,MAAM,cAAA,SAAsB,KAAA,CAAM;AAAA,EACvB,MAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EAEhB,WAAA,CAAY,QAAgB,IAAA,EAAc;AACxC,IAAA,MAAM,IAAA,GAAO,cAAA,CAAc,QAAA,CAAS,MAAM,CAAA;AAC1C,IAAA,KAAA,CAAM,sBAAsB,MAAM,CAAA,EAAA,EAAK,IAAI,CAAA,GAAA,EAAM,IAAA,IAAQ,SAAS,CAAA,CAAE,CAAA;AACpE,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AAAA,EAEA,OAAe,SAAS,MAAA,EAAuC;AAC7D,IAAA,IAAI,MAAA,KAAW,GAAA,IAAO,MAAA,KAAW,GAAA,EAAK,OAAO,MAAA;AAC7C,IAAA,IAAI,MAAA,KAAW,KAAK,OAAO,YAAA;AAC3B,IAAA,IAAI,MAAA,KAAW,KAAK,OAAO,mBAAA;AAC3B,IAAA,IAAI,MAAA,IAAU,GAAA,IAAO,MAAA,GAAS,GAAA,EAAK,OAAO,aAAA;AAC1C,IAAA,IAAI,MAAA,IAAU,KAAK,OAAO,QAAA;AAC1B,IAAA,OAAO,SAAA;AAAA,EACT;AACF;AAQO,IAAM,WAAN,MAAe;AAAA,EACH,OAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EAEjB,WAAA,CAAY,OAAA,GAA2B,EAAC,EAAG;AACzC,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACvE,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AACrB,IAAA,IAAA,CAAK,SAAA,GAAY,QAAQ,SAAA,IAAa,GAAA;AACtC,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC7C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAA,CAAK,SAAA,GAAY,QAAA,CAAS,IAAA,CAAK,UAAU,CAAA;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,MAAA,GAAkC;AACtC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAO,SAAS,CAAA;AAC/C,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,QAAQ,IAAI,CAAA;AACrD,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAA,GAAgC;AACpC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAO,QAAQ,CAAA;AAC9C,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,QAAQ,IAAI,CAAA;AACrD,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAS,KAAA,EAAiD;AAC9D,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,WAAA,EAAa;AAAA,MAClD,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,UAAU,KAAA,CAAM,QAAA;AAAA,QAChB,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,QAAQ,KAAA,CAAM;AAAA,OACf;AAAA,KACF,CAAA;AACD,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,KAAA,EAAqC;AAChD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,OAAO,KAAK,CAAA;AAE7C,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAElC,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,QAAQ,MAAA,EAAQ,CAAA,eAAA,EAAkB,MAAM,CAAA,CAAA,EAAI;AAAA,MACjE,IAAA,EAAM;AAAA,KACP,CAAA;AAED,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,KAAA,EAAwC;AACtD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,aAAA,EAAe;AAAA,MACpD,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,KAAK,SAAA,CAAU,EAAE,KAAK,KAAA,CAAM,GAAA,EAAK,QAAQ;AAAA,KAChD,CAAA;AACD,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,kBAAkB,KAAA,EAAwC;AAC9D,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AAEtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAElC,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,EAAE,QAAQ,CAAA;AAC7C,IAAA,IAAI,MAAM,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,MAAM,KAAK,CAAA;AACjD,IAAA,IAAI,MAAM,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,MAAM,OAAO,CAAA;AAEvD,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,CAAA,YAAA,EAAe,MAAA,CAAO,QAAA,EAAU,CAAA,CAAA,EAAI;AAAA,MACzE,IAAA,EAAM;AAAA,KACP,CAAA;AACD,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,YAAY,KAAA,EAA2C;AAC3D,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,IAAI,MAAM,KAAA,CAAM,MAAA,KAAW,GAAG,MAAM,IAAI,MAAM,4BAA4B,CAAA;AAC1E,IAAA,IAAI,MAAM,KAAA,CAAM,MAAA,GAAS,IAAI,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAErE,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AAC3C,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,SAAA,GAAY,CAAC,CAAA;AAChC,MAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,EAAI,QAAA,EAAU,MAAM,CAAA;AACjF,MAAA,IAAA,CAAK,MAAA,CAAO,OAAA,EAAS,IAAA,EAAM,QAAQ,CAAA;AAAA,IACrC;AAEA,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,QAAQ,MAAA,EAAQ,CAAA,qBAAA,EAAwB,MAAM,CAAA,CAAA,EAAI;AAAA,MACvE,IAAA,EAAM;AAAA,KACP,CAAA;AACD,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAK,KAAA,EAAmC;AAC5C,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,OAAA,EAAS,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAC9D,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,KAAA,EAAwC;AACtD,IAAA,MAAM,WAAA,GAAc,MAAM,WAAA,IAAe,IAAA;AACzC,IAAA,MAAM,MAAA,GACH,KAAA,CAAM,MAAA,KAAwC,WAAA,GAAc,KAAA,GAAQ,KAAA,CAAA;AACvE,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,OAAO,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,OAAA,IAAW,EAAE,CAAC,CAAA;AAClD,IAAA,IAAA,CAAK,MAAA,CAAO,aAAA,EAAe,WAAA,GAAc,MAAA,GAAS,OAAO,CAAA;AACzD,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,aAAA,EAAe,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACpE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,KAAA,EAAqC;AAChD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,MAAM,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,UAAA,EAAY,MAAM,OAAO,CAAA;AACxD,IAAA,IAAI,MAAM,KAAA,EAAO,IAAA,CAAK,MAAA,CAAO,QAAA,EAAU,MAAM,KAAK,CAAA;AAClD,IAAA,IAAI,MAAM,WAAA,EAAa,IAAA,CAAK,MAAA,CAAO,cAAA,EAAgB,MAAM,WAAW,CAAA;AACpE,IAAA,IAAI,KAAA,CAAM,kBAAkB,MAAA,EAAW,IAAA,CAAK,OAAO,iBAAA,EAAmB,MAAA,CAAO,KAAA,CAAM,aAAa,CAAC,CAAA;AACjG,IAAA,IAAI,KAAA,CAAM,kBAAkB,MAAA,EAAW,IAAA,CAAK,OAAO,iBAAA,EAAmB,MAAA,CAAO,KAAA,CAAM,aAAa,CAAC,CAAA;AACjG,IAAA,IAAI,KAAA,CAAM,eAAe,MAAA,EAAW,IAAA,CAAK,OAAO,aAAA,EAAe,MAAA,CAAO,KAAA,CAAM,UAAU,CAAC,CAAA;AACvF,IAAA,IAAI,KAAA,CAAM,kBAAkB,MAAA,EAAW,IAAA,CAAK,OAAO,gBAAA,EAAkB,MAAA,CAAO,KAAA,CAAM,aAAa,CAAC,CAAA;AAChG,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,SAAA,EAAW,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAChE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,KAAA,EAAsC;AAClD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,MAAM,WAAA,EAAa,IAAA,CAAK,MAAA,CAAO,cAAA,EAAgB,MAAM,WAAW,CAAA;AACpE,IAAA,IAAI,KAAA,CAAM,gBAAgB,MAAA,EAAW,IAAA,CAAK,OAAO,cAAA,EAAgB,MAAA,CAAO,KAAA,CAAM,WAAW,CAAC,CAAA;AAC1F,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,UAAA,EAAY,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACjE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,KAAA,EAAsC;AAClD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,MAAM,YAAA,EAAc,IAAA,CAAK,MAAA,CAAO,eAAA,EAAiB,MAAM,YAAY,CAAA;AACvE,IAAA,IAAI,KAAA,CAAM,iBAAiB,MAAA,EAAW,IAAA,CAAK,OAAO,eAAA,EAAiB,MAAA,CAAO,KAAA,CAAM,YAAY,CAAC,CAAA;AAC7F,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,UAAA,EAAY,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACjE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,KAAA,EAAyC;AACxD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,MAAM,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,UAAA,EAAY,MAAM,OAAO,CAAA;AACxD,IAAA,IAAI,MAAM,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,QAAA,EAAU,MAAM,MAAM,CAAA;AACpD,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,EAAW,IAAA,CAAK,OAAO,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AAC7E,IAAA,IAAI,KAAA,CAAM,WAAW,MAAA,EAAW,IAAA,CAAK,OAAO,QAAA,EAAU,KAAA,CAAM,MAAA,GAAS,MAAA,GAAS,OAAO,CAAA;AACrF,IAAA,IAAI,KAAA,CAAM,gBAAgB,MAAA,EAAW,IAAA,CAAK,OAAO,aAAA,EAAe,KAAA,CAAM,WAAA,GAAc,MAAA,GAAS,OAAO,CAAA;AACpG,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,cAAA,EAAgB,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACrE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,KAAA,EAAsC;AAClD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,UAAA,EAAY,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACjE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,SAAS,KAAA,EAAuC;AACpD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,MAAM,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,UAAA,EAAY,MAAM,OAAO,CAAA;AACxD,IAAA,IAAI,KAAA,CAAM,WAAW,MAAA,EAAW,IAAA,CAAK,OAAO,SAAA,EAAW,KAAA,CAAM,MAAA,GAAS,MAAA,GAAS,OAAO,CAAA;AACtF,IAAA,IAAI,KAAA,CAAM,eAAe,MAAA,EAAW,IAAA,CAAK,OAAO,aAAA,EAAe,MAAA,CAAO,KAAA,CAAM,UAAU,CAAC,CAAA;AACvF,IAAA,IAAI,MAAM,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,QAAA,EAAU,MAAM,MAAM,CAAA;AACpD,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,EAAW,IAAA,CAAK,OAAO,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AAC7E,IAAA,IAAI,KAAA,CAAM,iBAAiB,MAAA,EAAW,IAAA,CAAK,OAAO,gBAAA,EAAkB,MAAA,CAAO,KAAA,CAAM,YAAY,CAAC,CAAA;AAC9F,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,WAAA,EAAa,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAClE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,KAAA,EAAsC;AAClD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,OAAO,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,IAAU,GAAG,CAAC,CAAA;AAClD,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,UAAA,EAAY,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACjE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,QAAQ,KAAA,EAAsC;AAClD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,OAAO,OAAA,EAAS,MAAA,CAAO,KAAA,CAAM,KAAA,IAAS,CAAC,CAAC,CAAA;AAC7C,IAAA,IAAA,CAAK,MAAA,CAAO,OAAA,EAAS,KAAA,CAAM,KAAA,IAAS,SAAS,CAAA;AAC7C,IAAA,IAAI,KAAA,CAAM,gBAAgB,MAAA,EAAW;AACnC,MAAA,IAAA,CAAK,MAAA,CAAO,cAAA,EAAgB,KAAA,CAAM,WAAA,GAAc,SAAS,OAAO,CAAA;AAAA,IAClE;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,UAAA,EAAY,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACjE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,KAAA,EAA0C;AAC1D,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,KAAA,CAAM,mBAAmB,MAAA,EAAW;AACtC,MAAA,IAAA,CAAK,MAAA,CAAO,kBAAA,EAAoB,KAAA,CAAM,cAAA,GAAiB,SAAS,OAAO,CAAA;AAAA,IACzE;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,eAAA,EAAiB,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACtE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,SAAS,KAAA,EAAuC;AACpD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,WAAA,EAAa,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAClE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,QAAQ,KAAA,EAAsC;AAClD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,KAAA,CAAM,SAAS,MAAA,EAAW;AAC5B,MAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAU,YAAA,EAAa,GAAI,MAAM,MAAA,CAAO;AAAA,QAC9D,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,QAAA,EAAU,MAAM,YAAA,IAAgB;AAAA,OACjC,CAAA;AACD,MAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,YAAY,CAAA;AAAA,IAC5C;AACA,IAAA,IAAI,MAAM,IAAA,EAAM;AACd,MAAA,IAAA,CAAK,OAAO,GAAA,EAAK,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAC,CAAA;AACrC,MAAA,IAAA,CAAK,OAAO,GAAA,EAAK,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAC,CAAA;AACrC,MAAA,IAAA,CAAK,OAAO,GAAA,EAAK,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAC,CAAA;AACrC,MAAA,IAAA,CAAK,OAAO,GAAA,EAAK,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,IACvC;AACA,IAAA,IAAI,KAAA,CAAM,aAAa,MAAA,EAAW,IAAA,CAAK,OAAO,UAAA,EAAY,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAC,CAAA;AAChF,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,UAAA,EAAY,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACjE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,WAAW,KAAA,EAAyC;AACxD,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,QAAA,EAAU,KAAA,CAAM,UAAU,CAAA;AACtF,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAClC,IAAA,IAAI,MAAM,YAAA,EAAc,IAAA,CAAK,MAAA,CAAO,eAAA,EAAiB,MAAM,YAAY,CAAA;AACvE,IAAA,IAAI,MAAM,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,UAAA,EAAY,MAAM,OAAO,CAAA;AACxD,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,MAAM,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,aAAA,EAAe,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACpE,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,GAAA,CAAI,aAAa,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,eAAe,KAAA,EAA8C;AACjE,IAAA,MAAM,MAAA,GAAuB,MAAM,MAAA,IAAU,KAAA;AAC7C,IAAA,IAAI,MAAM,IAAA,CAAK,MAAA,KAAW,GAAG,MAAM,IAAI,MAAM,2BAA2B,CAAA;AACxE,IAAA,IAAI,MAAM,IAAA,CAAK,MAAA,GAAS,IAAI,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAEnE,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,QAAQ,mBAAA,EAAqB;AAAA,MAC1D,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,KAAK,SAAA,CAAU,EAAE,MAAM,KAAA,CAAM,IAAA,EAAM,QAAQ;AAAA,KAClD,CAAA;AACD,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACjE,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB;AAAA,EAEA,MAAc,OAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,GAA8D,EAAC,EAC5C;AACnB,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,IAAI,CAAA,CAAA;AAClC,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,YAAA,EAAc,oBAAoB,WAAW,CAAA,CAAA;AAAA,MAC7C,GAAI,IAAA,CAAK,OAAA,IAAW;AAAC,KACvB;AACA,IAAA,IAAI,KAAK,KAAA,EAAO,OAAA,CAAQ,eAAe,CAAA,GAAI,CAAA,OAAA,EAAU,KAAK,KAAK,CAAA,CAAA;AAE/D,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,SAAS,CAAA;AACjE,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,IAAA,CAAK,SAAA,CAAU,GAAA,EAAK;AAAA,QAC/B,MAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF;AACF;AAEA,eAAe,OACb,KAAA,EAC2C;AAC3C,EAAA,MAAM,EAAE,MAAK,GAAI,KAAA;AACjB,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,IAAY,aAAA,CAAc,IAAI,CAAA;AAErD,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,IAAA,MAAM,IAAA,GAAO,MAAMA,iBAAA,CAAS,IAAI,CAAA;AAChC,IAAA,OAAO,EAAE,IAAA,EAAM,IAAI,IAAA,CAAK,CAAC,IAAI,UAAA,CAAW,IAAI,CAAC,CAAC,CAAA,EAAG,QAAA,EAAS;AAAA,EAC5D;AACA,EAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,QAAA,EAAS;AAAA,EAChC;AACA,EAAA,IAAI,gBAAgB,WAAA,EAAa;AAC/B,IAAA,OAAO,EAAE,IAAA,EAAM,IAAI,IAAA,CAAK,CAAC,IAAI,UAAA,CAAW,IAAI,CAAC,CAAC,CAAA,EAAG,QAAA,EAAS;AAAA,EAC5D;AACA,EAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,IAAA,OAAO,EAAE,IAAA,EAAM,IAAI,IAAA,CAAK,CAAC,IAAI,UAAA,CAAW,IAAI,CAAC,CAAC,CAAA,EAAG,QAAA,EAAS;AAAA,EAC5D;AACA,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,EAAG;AACzB,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,IAAI,IAAA,CAAK,CAAC,IAAI,UAAA,CAAW,IAAI,CAAC,CAAC,CAAA;AAAA,MACrC;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,IAAI,UAAU,qFAAqF,CAAA;AAC3G;AAEA,SAAS,cAAc,IAAA,EAAiE;AACtF,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,EAAU,OAAOC,aAAA,CAAS,IAAI,CAAA,IAAK,OAAA;AACvD,EAAA,OAAO,OAAA;AACT;AAEA,IAAO,aAAA,GAAQ","file":"index.cjs","sourcesContent":["/**\r\n * @useknockout/node — official TypeScript / Node.js client for the useknockout API.\r\n *\r\n * Quick start:\r\n *\r\n * import { Knockout } from \"@useknockout/node\";\r\n *\r\n * const client = new Knockout({ token: process.env.KNOCKOUT_TOKEN! });\r\n * const png = await client.remove({ file: \"./input.jpg\" }); // Buffer of PNG bytes\r\n * await writeFile(\"out.png\", png);\r\n */\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://useknockout--api.modal.run\";\r\nconst SDK_VERSION = \"0.3.0\";\r\n\r\nexport type OutputFormat = \"png\" | \"webp\";\r\nexport type OpaqueFormat = \"png\" | \"webp\" | \"jpg\";\r\n\r\ntype FileInput = string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n\r\nexport interface KnockoutOptions {\r\n /** API bearer token. Required unless your self-hosted instance has no auth. */\r\n token?: string;\r\n /** Override the API base URL. Defaults to the hosted endpoint. */\r\n baseUrl?: string;\r\n /** Per-request timeout in milliseconds. Default 60_000. */\r\n timeoutMs?: number;\r\n /** Custom fetch (useful for edge runtimes / polyfills). Defaults to global fetch. */\r\n fetch?: typeof fetch;\r\n}\r\n\r\nexport interface RemoveInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface RemoveUrlInput {\r\n /** Remote URL of the image to process. */\r\n url: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface ReplaceBgInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the foreground image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Hex color for the new background. Default \"#FFFFFF\". Ignored if `bgUrl` is set. */\r\n bgColor?: string;\r\n /** Remote URL of an image to use as the new background. Takes precedence over `bgColor`. */\r\n bgUrl?: string;\r\n /** Output format. \"jpg\" is smallest. Default \"png\". */\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface BatchInput {\r\n /** Array of local paths / buffers / blobs. Up to 10. */\r\n files: Array<string | Buffer | Blob | ArrayBuffer | Uint8Array>;\r\n /** Optional filenames aligned to `files`. */\r\n filenames?: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchUrlInput {\r\n /** Remote URLs to process. Up to 10. */\r\n urls: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchResultItem {\r\n filename?: string;\r\n url?: string;\r\n success: boolean;\r\n format?: OutputFormat;\r\n size_bytes?: number;\r\n data_base64?: string;\r\n error?: string;\r\n}\r\n\r\nexport interface BatchResponse {\r\n count: number;\r\n format: OutputFormat;\r\n results: BatchResultItem[];\r\n}\r\n\r\nexport interface MaskInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface SmartCropInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Padding around the subject bbox, in pixels. Default 24. */\r\n padding?: number;\r\n /** Return transparent cutout (true) or cropped region from original (false). Default true. */\r\n transparent?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ShadowInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n bgUrl?: string;\r\n shadowColor?: string;\r\n shadowOffsetX?: number;\r\n shadowOffsetY?: number;\r\n shadowBlur?: number;\r\n shadowOpacity?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface StickerInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#FFFFFF\". */\r\n strokeColor?: string;\r\n /** Outline width in pixels. Default 20. */\r\n strokeWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface OutlineInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#000000\". */\r\n outlineColor?: string;\r\n /** Outline width in pixels. Default 4. */\r\n outlineWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface StudioShotInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n /** e.g. \"1:1\", \"4:5\", \"16:9\". Default \"1:1\". */\r\n aspect?: string;\r\n padding?: number;\r\n shadow?: boolean;\r\n /** Keep a transparent background. Ignores bgColor and shadow; output is PNG (jpg is coerced). */\r\n transparent?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface CompareInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface HeadshotInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the background. Default \"#FFFFFF\". Ignored if `bgBlur` is true. */\r\n bgColor?: string;\r\n /** Use a blurred copy of the original image as the background. Default false. */\r\n bgBlur?: boolean;\r\n /** Gaussian blur radius for the background when `bgBlur` is true. Default 20. */\r\n blurRadius?: number;\r\n /** Output aspect \"W:H\". Default \"4:5\" (portrait). */\r\n aspect?: string;\r\n /** Padding around the subject bbox, in pixels. Default 64. */\r\n padding?: number;\r\n /** Vertical headroom as a ratio of canvas height (0–0.5). Default 0.18. */\r\n headTopRatio?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface PreviewInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Long-edge cap in pixels (64–1024). Default 512. */\r\n maxDim?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface UpscaleInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Upscale factor: 2 or 4. Default 4. */\r\n scale?: 2 | 4;\r\n /**\r\n * Backend. `swin2sr` (default, v0.6.0+) is sharper on real photos.\r\n * `realesrgan` is the legacy backend — better on anime/illustrations.\r\n */\r\n model?: \"swin2sr\" | \"realesrgan\";\r\n /** Route through GFPGAN to fix facial detail. Slower, use for portraits. Implies realesrgan. */\r\n faceEnhance?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface FaceRestoreInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Restore only the most prominent face (faster). Default false (all faces). */\r\n onlyCenterFace?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ColorizeInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface SilhouetteInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the subject silhouette. Default \"#7C3AED\" (purple). */\r\n subjectColor?: string;\r\n /** Hex color for the background. Default \"#FFFFFF\" (white). */\r\n bgColor?: string;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface InpaintBbox {\r\n x: number;\r\n y: number;\r\n w: number;\r\n h: number;\r\n}\r\n\r\nexport interface InpaintInput {\r\n file: FileInput;\r\n filename?: string;\r\n /**\r\n * Optional mask. White pixels = inpaint, black = keep. If omitted (and no\r\n * `bbox`), auto-subject mode runs BiRefNet on the input and inverts the\r\n * subject mask.\r\n */\r\n mask?: FileInput;\r\n maskFilename?: string;\r\n /** Rectangular region to inpaint. Mutually exclusive with `mask`. */\r\n bbox?: InpaintBbox;\r\n /** Mask dilation in pixels. Default 8, range 0..32. */\r\n dilation?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface EstimateInput {\r\n /** Endpoint name without leading slash, e.g. \"remove\" or \"headshot\". */\r\n endpoint: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface EstimateResponse {\r\n endpoint: string;\r\n image_pixels: number;\r\n est_latency_ms_warm: number;\r\n est_latency_ms_cold: number;\r\n est_cost_usd: number;\r\n free_during_beta: boolean;\r\n note: string;\r\n}\r\n\r\nexport interface StatsDay {\r\n date: string;\r\n count: number;\r\n}\r\n\r\nexport interface StatsResponse {\r\n total_processed: number;\r\n today: number;\r\n last_7_days: StatsDay[];\r\n error?: string;\r\n detail?: string;\r\n}\r\n\r\nexport interface HealthResponse {\r\n status: string;\r\n model: string;\r\n}\r\n\r\n/**\r\n * Error thrown when the API returns a non-2xx response.\r\n */\r\nexport class KnockoutError extends Error {\r\n public readonly status: number;\r\n public readonly code: \"auth\" | \"rate_limit\" | \"bad_request\" | \"payload_too_large\" | \"server\" | \"unknown\";\r\n public readonly body: string;\r\n\r\n constructor(status: number, body: string) {\r\n const code = KnockoutError.classify(status);\r\n super(`Knockout API error ${status} (${code}): ${body || \"no body\"}`);\r\n this.name = \"KnockoutError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n }\r\n\r\n private static classify(status: number): KnockoutError[\"code\"] {\r\n if (status === 401 || status === 403) return \"auth\";\r\n if (status === 429) return \"rate_limit\";\r\n if (status === 413) return \"payload_too_large\";\r\n if (status >= 400 && status < 500) return \"bad_request\";\r\n if (status >= 500) return \"server\";\r\n return \"unknown\";\r\n }\r\n}\r\n\r\n/**\r\n * useknockout API client.\r\n *\r\n * All methods return a `Buffer` (Node) of the processed image bytes.\r\n * Use `.toString(\"base64\")` or `writeFile(path, buf)` to persist.\r\n */\r\nexport class Knockout {\r\n private readonly baseUrl: string;\r\n private readonly token: string | undefined;\r\n private readonly timeoutMs: number;\r\n private readonly fetchImpl: typeof fetch;\r\n\r\n constructor(options: KnockoutOptions = {}) {\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\r\n this.token = options.token;\r\n this.timeoutMs = options.timeoutMs ?? 60_000;\r\n const fetchRef = options.fetch ?? globalThis.fetch;\r\n if (!fetchRef) {\r\n throw new Error(\r\n \"Global fetch is unavailable. Provide `options.fetch` or use Node 18+.\"\r\n );\r\n }\r\n this.fetchImpl = fetchRef.bind(globalThis);\r\n }\r\n\r\n /** Hit GET /health — no auth required. */\r\n async health(): Promise<HealthResponse> {\r\n const res = await this.request(\"GET\", \"/health\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as HealthResponse;\r\n }\r\n\r\n /**\r\n * Public usage counter — total images processed all-time, today, and a 7-day breakdown.\r\n * Use for landing-page social proof. Eventually consistent across containers.\r\n */\r\n async stats(): Promise<StatsResponse> {\r\n const res = await this.request(\"GET\", \"/stats\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as StatsResponse;\r\n }\r\n\r\n /**\r\n * Predict latency + cost for an endpoint and image size without doing any GPU work.\r\n *\r\n * @example\r\n * const est = await client.estimate({ endpoint: \"remove\", width: 1024, height: 1024 });\r\n */\r\n async estimate(input: EstimateInput): Promise<EstimateResponse> {\r\n const res = await this.request(\"POST\", \"/estimate\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n endpoint: input.endpoint,\r\n width: input.width,\r\n height: input.height,\r\n }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as EstimateResponse;\r\n }\r\n\r\n /**\r\n * Remove the background from an image, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.remove({ file: \"./input.jpg\" });\r\n */\r\n async remove(input: RemoveInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob(input);\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const res = await this.request(\"POST\", `/remove?format=${format}`, {\r\n body: form,\r\n });\r\n\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from a remote URL, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.removeUrl({ url: \"https://example.com/cat.jpg\" });\r\n */\r\n async removeUrl(input: RemoveUrlInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const res = await this.request(\"POST\", \"/remove-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ url: input.url, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Replace the background with a solid color or a remote image.\r\n *\r\n * @example Solid color\r\n * const jpg = await client.replaceBackground({ file: \"./cat.jpg\", bgColor: \"#FF5733\", format: \"jpg\" });\r\n *\r\n * @example Remote image as new background\r\n * const png = await client.replaceBackground({\r\n * file: \"./cat.jpg\",\r\n * bgUrl: \"https://example.com/beach.jpg\",\r\n * });\r\n */\r\n async replaceBackground(input: ReplaceBgInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const params = new URLSearchParams({ format });\r\n if (input.bgUrl) params.set(\"bg_url\", input.bgUrl);\r\n if (input.bgColor) params.set(\"bg_color\", input.bgColor);\r\n\r\n const res = await this.request(\"POST\", `/replace-bg?${params.toString()}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 images in a single call.\r\n * Returns a JSON object with base64-encoded result bytes per image.\r\n *\r\n * @example\r\n * const batch = await client.removeBatch({\r\n * files: [\"./a.jpg\", \"./b.jpg\", \"./c.jpg\"],\r\n * format: \"png\",\r\n * });\r\n * for (const r of batch.results) {\r\n * if (r.success) await writeFile(r.filename!, Buffer.from(r.data_base64!, \"base64\"));\r\n * }\r\n */\r\n async removeBatch(input: BatchInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.files.length === 0) throw new Error(\"At least one file required\");\r\n if (input.files.length > 10) throw new Error(\"Max 10 files per batch\");\r\n\r\n const form = new FormData();\r\n for (let i = 0; i < input.files.length; i++) {\r\n const name = input.filenames?.[i];\r\n const { blob, filename } = await toBlob({ file: input.files[i]!, filename: name });\r\n form.append(\"files\", blob, filename);\r\n }\r\n\r\n const res = await this.request(\"POST\", `/remove-batch?format=${format}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n /**\r\n * Return only the alpha mask as a grayscale PNG/WebP.\r\n * Useful when chaining into your own compositing pipeline.\r\n */\r\n async mask(input: MaskInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/mask\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Auto-crop to the subject's tight bounding box with configurable padding.\r\n * Returns either a transparent cutout or a cropped region from the original image.\r\n */\r\n async smartCrop(input: SmartCropInput): Promise<Buffer> {\r\n const transparent = input.transparent ?? true;\r\n const format: OpaqueFormat =\r\n (input.format as OpaqueFormat | undefined) ?? (transparent ? \"png\" : \"jpg\");\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"padding\", String(input.padding ?? 24));\r\n form.append(\"transparent\", transparent ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/smart-crop\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Composite subject onto a new background with a configurable drop shadow.\r\n */\r\n async shadow(input: ShadowInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgUrl) form.append(\"bg_url\", input.bgUrl);\r\n if (input.shadowColor) form.append(\"shadow_color\", input.shadowColor);\r\n if (input.shadowOffsetX !== undefined) form.append(\"shadow_offset_x\", String(input.shadowOffsetX));\r\n if (input.shadowOffsetY !== undefined) form.append(\"shadow_offset_y\", String(input.shadowOffsetY));\r\n if (input.shadowBlur !== undefined) form.append(\"shadow_blur\", String(input.shadowBlur));\r\n if (input.shadowOpacity !== undefined) form.append(\"shadow_opacity\", String(input.shadowOpacity));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/shadow\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Sticker style — subject with a thick outline on transparent background.\r\n * Perfect for WhatsApp / iMessage / Telegram stickers.\r\n */\r\n async sticker(input: StickerInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.strokeColor) form.append(\"stroke_color\", input.strokeColor);\r\n if (input.strokeWidth !== undefined) form.append(\"stroke_width\", String(input.strokeWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/sticker\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Subject on transparent background with a thin configurable outline.\r\n */\r\n async outline(input: OutlineInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.outlineColor) form.append(\"outline_color\", input.outlineColor);\r\n if (input.outlineWidth !== undefined) form.append(\"outline_width\", String(input.outlineWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/outline\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * E-commerce preset — cutout + tight crop + centered + optional shadow on a standard aspect canvas.\r\n */\r\n async studioShot(input: StudioShotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.shadow !== undefined) form.append(\"shadow\", input.shadow ? \"true\" : \"false\");\r\n if (input.transparent !== undefined) form.append(\"transparent\", input.transparent ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/studio-shot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Before/after side-by-side preview — original on left, transparent cutout (on a checkerboard) on right.\r\n */\r\n async compare(input: CompareInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/compare\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * LinkedIn-ready headshot preset — bg removal + portrait crop + center face + bg color or blur.\r\n *\r\n * @example Solid bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgColor: \"#0A0A0A\" });\r\n *\r\n * @example Blurred original as bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgBlur: true, blurRadius: 24 });\r\n */\r\n async headshot(input: HeadshotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgBlur !== undefined) form.append(\"bg_blur\", input.bgBlur ? \"true\" : \"false\");\r\n if (input.blurRadius !== undefined) form.append(\"blur_radius\", String(input.blurRadius));\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.headTopRatio !== undefined) form.append(\"head_top_ratio\", String(input.headTopRatio));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/headshot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Fast low-res preview cutout (~80ms warm). Skips pymatting refinement and downscales\r\n * input to `maxDim` (default 512px on the long edge). Use for UX progress indicators.\r\n */\r\n async preview(input: PreviewInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"max_dim\", String(input.maxDim ?? 512));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/preview\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * 2x / 4x super-resolution. Defaults to Swin2SR (SwinV2 transformer) for sharper\r\n * detail on real photos. Pass `model: \"realesrgan\"` for the legacy backend\r\n * (better on anime / illustrations). `faceEnhance: true` routes portraits\r\n * through GFPGAN.\r\n *\r\n * @example Cutout → 4x upscale (print-ready)\r\n * const png = await client.remove({ file: \"./photo.jpg\" });\r\n * const big = await client.upscale({ file: png, scale: 4 });\r\n */\r\n async upscale(input: UpscaleInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"scale\", String(input.scale ?? 4));\r\n form.append(\"model\", input.model ?? \"swin2sr\");\r\n if (input.faceEnhance !== undefined) {\r\n form.append(\"face_enhance\", input.faceEnhance ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/upscale\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * GFPGAN v1.4 portrait restoration — fix blurry / damaged / low-res faces.\r\n * Background is upscaled 2x by Real-ESRGAN. Pairs with /headshot.\r\n */\r\n async faceRestore(input: FaceRestoreInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.onlyCenterFace !== undefined) {\r\n form.append(\"only_center_face\", input.onlyCenterFace ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/face-restore\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Colorize a black-and-white or grayscale photo (DDColor v0.7.0+).\r\n *\r\n * Apache-2.0 model, single feed-forward (no diffusion), ~500ms warm on L4.\r\n * Works on any input — color photos are converted to grayscale internally\r\n * before color is predicted, which makes round-trip recoloring easy too.\r\n *\r\n * @example\r\n * const buf = await client.colorize({ file: \"./old-photo.jpg\" });\r\n * await writeFile(\"colorized.png\", buf);\r\n */\r\n async colorize(input: ColorizeInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/colorize\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Inpaint a region of an image using LaMa (Apache-2.0). Three modes,\r\n * auto-detected from what you pass:\r\n *\r\n * 1. **auto-subject** — pass only `file`. BiRefNet derives the subject\r\n * mask, inverts it, and LaMa fills the subject region with plausible\r\n * background. Drop in a photo, get the subject erased.\r\n * 2. **mask** — pass `file` + `mask` (any image, white = inpaint).\r\n * 3. **bbox** — pass `file` + `bbox: { x, y, w, h }`. Rectangular region.\r\n *\r\n * `dilation` (0..32, default 8) expands the mask before LaMa runs to\r\n * reduce ghost outlines from tight masks.\r\n *\r\n * @example Auto-erase subject\r\n * const png = await client.inpaint({ file: \"./photo.jpg\" });\r\n *\r\n * @example Erase a rectangular region\r\n * const png = await client.inpaint({ file: \"./photo.jpg\", bbox: { x: 100, y: 100, w: 300, h: 400 } });\r\n */\r\n async inpaint(input: InpaintInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.mask !== undefined) {\r\n const { blob: maskBlob, filename: maskFilename } = await toBlob({\r\n file: input.mask,\r\n filename: input.maskFilename ?? \"mask.png\",\r\n });\r\n form.append(\"mask\", maskBlob, maskFilename);\r\n }\r\n if (input.bbox) {\r\n form.append(\"x\", String(input.bbox.x));\r\n form.append(\"y\", String(input.bbox.y));\r\n form.append(\"w\", String(input.bbox.w));\r\n form.append(\"h\", String(input.bbox.h));\r\n }\r\n if (input.dilation !== undefined) form.append(\"dilation\", String(input.dilation));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/inpaint\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Two-tone silhouette portrait — subject in one solid color, bg in another.\r\n * Apple Music / Spotify avatar aesthetic. Reuses BiRefNet mask path; no new\r\n * model load, fast.\r\n *\r\n * @example\r\n * const png = await client.silhouette({\r\n * file: \"./portrait.jpg\",\r\n * subjectColor: \"#1E2960\",\r\n * bgColor: \"#F0857C\",\r\n * });\r\n */\r\n async silhouette(input: SilhouetteInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.subjectColor) form.append(\"subject_color\", input.subjectColor);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/silhouette\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 remote image URLs in a single call.\r\n *\r\n * @example\r\n * const batch = await client.removeBatchUrl({\r\n * urls: [\"https://a.jpg\", \"https://b.jpg\"],\r\n * });\r\n */\r\n async removeBatchUrl(input: BatchUrlInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.urls.length === 0) throw new Error(\"At least one URL required\");\r\n if (input.urls.length > 10) throw new Error(\"Max 10 URLs per batch\");\r\n\r\n const res = await this.request(\"POST\", \"/remove-batch-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ urls: input.urls, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n private async request(\r\n method: \"GET\" | \"POST\",\r\n path: string,\r\n init: { headers?: Record<string, string>; body?: BodyInit } = {}\r\n ): Promise<Response> {\r\n const url = `${this.baseUrl}${path}`;\r\n const headers: Record<string, string> = {\r\n \"User-Agent\": `useknockout-node/${SDK_VERSION}`,\r\n ...(init.headers ?? {}),\r\n };\r\n if (this.token) headers[\"Authorization\"] = `Bearer ${this.token}`;\r\n\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n try {\r\n return await this.fetchImpl(url, {\r\n method,\r\n headers,\r\n body: init.body,\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n\r\nasync function toBlob(\r\n input: { file: string | Buffer | Blob | ArrayBuffer | Uint8Array; filename?: string }\r\n): Promise<{ blob: Blob; filename: string }> {\r\n const { file } = input;\r\n const filename = input.filename ?? inferFilename(file);\r\n\r\n if (typeof file === \"string\") {\r\n const data = await readFile(file);\r\n return { blob: new Blob([new Uint8Array(data)]), filename };\r\n }\r\n if (file instanceof Blob) {\r\n return { blob: file, filename };\r\n }\r\n if (file instanceof ArrayBuffer) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (file instanceof Uint8Array) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (Buffer.isBuffer(file)) {\r\n return {\r\n blob: new Blob([new Uint8Array(file)]),\r\n filename,\r\n };\r\n }\r\n throw new TypeError(\"Unsupported `file` input. Provide a path, Buffer, Blob, ArrayBuffer, or Uint8Array.\");\r\n}\r\n\r\nfunction inferFilename(file: string | Buffer | Blob | ArrayBuffer | Uint8Array): string {\r\n if (typeof file === \"string\") return basename(file) || \"image\";\r\n return \"image\";\r\n}\r\n\r\nexport default Knockout;\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -129,6 +129,8 @@ interface StudioShotInput {
|
|
|
129
129
|
aspect?: string;
|
|
130
130
|
padding?: number;
|
|
131
131
|
shadow?: boolean;
|
|
132
|
+
/** Keep a transparent background. Ignores bgColor and shadow; output is PNG (jpg is coerced). */
|
|
133
|
+
transparent?: boolean;
|
|
132
134
|
format?: OpaqueFormat;
|
|
133
135
|
}
|
|
134
136
|
interface CompareInput {
|
|
@@ -186,6 +188,37 @@ interface ColorizeInput {
|
|
|
186
188
|
filename?: string;
|
|
187
189
|
format?: OpaqueFormat;
|
|
188
190
|
}
|
|
191
|
+
interface SilhouetteInput {
|
|
192
|
+
file: FileInput;
|
|
193
|
+
filename?: string;
|
|
194
|
+
/** Hex color for the subject silhouette. Default "#7C3AED" (purple). */
|
|
195
|
+
subjectColor?: string;
|
|
196
|
+
/** Hex color for the background. Default "#FFFFFF" (white). */
|
|
197
|
+
bgColor?: string;
|
|
198
|
+
format?: OpaqueFormat;
|
|
199
|
+
}
|
|
200
|
+
interface InpaintBbox {
|
|
201
|
+
x: number;
|
|
202
|
+
y: number;
|
|
203
|
+
w: number;
|
|
204
|
+
h: number;
|
|
205
|
+
}
|
|
206
|
+
interface InpaintInput {
|
|
207
|
+
file: FileInput;
|
|
208
|
+
filename?: string;
|
|
209
|
+
/**
|
|
210
|
+
* Optional mask. White pixels = inpaint, black = keep. If omitted (and no
|
|
211
|
+
* `bbox`), auto-subject mode runs BiRefNet on the input and inverts the
|
|
212
|
+
* subject mask.
|
|
213
|
+
*/
|
|
214
|
+
mask?: FileInput;
|
|
215
|
+
maskFilename?: string;
|
|
216
|
+
/** Rectangular region to inpaint. Mutually exclusive with `mask`. */
|
|
217
|
+
bbox?: InpaintBbox;
|
|
218
|
+
/** Mask dilation in pixels. Default 8, range 0..32. */
|
|
219
|
+
dilation?: number;
|
|
220
|
+
format?: OpaqueFormat;
|
|
221
|
+
}
|
|
189
222
|
interface EstimateInput {
|
|
190
223
|
/** Endpoint name without leading slash, e.g. "remove" or "headshot". */
|
|
191
224
|
endpoint: string;
|
|
@@ -367,6 +400,39 @@ declare class Knockout {
|
|
|
367
400
|
* await writeFile("colorized.png", buf);
|
|
368
401
|
*/
|
|
369
402
|
colorize(input: ColorizeInput): Promise<Buffer>;
|
|
403
|
+
/**
|
|
404
|
+
* Inpaint a region of an image using LaMa (Apache-2.0). Three modes,
|
|
405
|
+
* auto-detected from what you pass:
|
|
406
|
+
*
|
|
407
|
+
* 1. **auto-subject** — pass only `file`. BiRefNet derives the subject
|
|
408
|
+
* mask, inverts it, and LaMa fills the subject region with plausible
|
|
409
|
+
* background. Drop in a photo, get the subject erased.
|
|
410
|
+
* 2. **mask** — pass `file` + `mask` (any image, white = inpaint).
|
|
411
|
+
* 3. **bbox** — pass `file` + `bbox: { x, y, w, h }`. Rectangular region.
|
|
412
|
+
*
|
|
413
|
+
* `dilation` (0..32, default 8) expands the mask before LaMa runs to
|
|
414
|
+
* reduce ghost outlines from tight masks.
|
|
415
|
+
*
|
|
416
|
+
* @example Auto-erase subject
|
|
417
|
+
* const png = await client.inpaint({ file: "./photo.jpg" });
|
|
418
|
+
*
|
|
419
|
+
* @example Erase a rectangular region
|
|
420
|
+
* const png = await client.inpaint({ file: "./photo.jpg", bbox: { x: 100, y: 100, w: 300, h: 400 } });
|
|
421
|
+
*/
|
|
422
|
+
inpaint(input: InpaintInput): Promise<Buffer>;
|
|
423
|
+
/**
|
|
424
|
+
* Two-tone silhouette portrait — subject in one solid color, bg in another.
|
|
425
|
+
* Apple Music / Spotify avatar aesthetic. Reuses BiRefNet mask path; no new
|
|
426
|
+
* model load, fast.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* const png = await client.silhouette({
|
|
430
|
+
* file: "./portrait.jpg",
|
|
431
|
+
* subjectColor: "#1E2960",
|
|
432
|
+
* bgColor: "#F0857C",
|
|
433
|
+
* });
|
|
434
|
+
*/
|
|
435
|
+
silhouette(input: SilhouetteInput): Promise<Buffer>;
|
|
370
436
|
/**
|
|
371
437
|
* Remove the background from up to 10 remote image URLs in a single call.
|
|
372
438
|
*
|
|
@@ -379,4 +445,4 @@ declare class Knockout {
|
|
|
379
445
|
private request;
|
|
380
446
|
}
|
|
381
447
|
|
|
382
|
-
export { type BatchInput, type BatchResponse, type BatchResultItem, type BatchUrlInput, type ColorizeInput, type CompareInput, DEFAULT_BASE_URL, type EstimateInput, type EstimateResponse, type FaceRestoreInput, type HeadshotInput, type HealthResponse, Knockout, KnockoutError, type KnockoutOptions, type MaskInput, type OpaqueFormat, type OutlineInput, type OutputFormat, type PreviewInput, type RemoveInput, type RemoveUrlInput, type ReplaceBgInput, type ShadowInput, type SmartCropInput, type StatsDay, type StatsResponse, type StickerInput, type StudioShotInput, type UpscaleInput, Knockout as default };
|
|
448
|
+
export { type BatchInput, type BatchResponse, type BatchResultItem, type BatchUrlInput, type ColorizeInput, type CompareInput, DEFAULT_BASE_URL, type EstimateInput, type EstimateResponse, type FaceRestoreInput, type HeadshotInput, type HealthResponse, type InpaintBbox, type InpaintInput, Knockout, KnockoutError, type KnockoutOptions, type MaskInput, type OpaqueFormat, type OutlineInput, type OutputFormat, type PreviewInput, type RemoveInput, type RemoveUrlInput, type ReplaceBgInput, type ShadowInput, type SilhouetteInput, type SmartCropInput, type StatsDay, type StatsResponse, type StickerInput, type StudioShotInput, type UpscaleInput, Knockout as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -129,6 +129,8 @@ interface StudioShotInput {
|
|
|
129
129
|
aspect?: string;
|
|
130
130
|
padding?: number;
|
|
131
131
|
shadow?: boolean;
|
|
132
|
+
/** Keep a transparent background. Ignores bgColor and shadow; output is PNG (jpg is coerced). */
|
|
133
|
+
transparent?: boolean;
|
|
132
134
|
format?: OpaqueFormat;
|
|
133
135
|
}
|
|
134
136
|
interface CompareInput {
|
|
@@ -186,6 +188,37 @@ interface ColorizeInput {
|
|
|
186
188
|
filename?: string;
|
|
187
189
|
format?: OpaqueFormat;
|
|
188
190
|
}
|
|
191
|
+
interface SilhouetteInput {
|
|
192
|
+
file: FileInput;
|
|
193
|
+
filename?: string;
|
|
194
|
+
/** Hex color for the subject silhouette. Default "#7C3AED" (purple). */
|
|
195
|
+
subjectColor?: string;
|
|
196
|
+
/** Hex color for the background. Default "#FFFFFF" (white). */
|
|
197
|
+
bgColor?: string;
|
|
198
|
+
format?: OpaqueFormat;
|
|
199
|
+
}
|
|
200
|
+
interface InpaintBbox {
|
|
201
|
+
x: number;
|
|
202
|
+
y: number;
|
|
203
|
+
w: number;
|
|
204
|
+
h: number;
|
|
205
|
+
}
|
|
206
|
+
interface InpaintInput {
|
|
207
|
+
file: FileInput;
|
|
208
|
+
filename?: string;
|
|
209
|
+
/**
|
|
210
|
+
* Optional mask. White pixels = inpaint, black = keep. If omitted (and no
|
|
211
|
+
* `bbox`), auto-subject mode runs BiRefNet on the input and inverts the
|
|
212
|
+
* subject mask.
|
|
213
|
+
*/
|
|
214
|
+
mask?: FileInput;
|
|
215
|
+
maskFilename?: string;
|
|
216
|
+
/** Rectangular region to inpaint. Mutually exclusive with `mask`. */
|
|
217
|
+
bbox?: InpaintBbox;
|
|
218
|
+
/** Mask dilation in pixels. Default 8, range 0..32. */
|
|
219
|
+
dilation?: number;
|
|
220
|
+
format?: OpaqueFormat;
|
|
221
|
+
}
|
|
189
222
|
interface EstimateInput {
|
|
190
223
|
/** Endpoint name without leading slash, e.g. "remove" or "headshot". */
|
|
191
224
|
endpoint: string;
|
|
@@ -367,6 +400,39 @@ declare class Knockout {
|
|
|
367
400
|
* await writeFile("colorized.png", buf);
|
|
368
401
|
*/
|
|
369
402
|
colorize(input: ColorizeInput): Promise<Buffer>;
|
|
403
|
+
/**
|
|
404
|
+
* Inpaint a region of an image using LaMa (Apache-2.0). Three modes,
|
|
405
|
+
* auto-detected from what you pass:
|
|
406
|
+
*
|
|
407
|
+
* 1. **auto-subject** — pass only `file`. BiRefNet derives the subject
|
|
408
|
+
* mask, inverts it, and LaMa fills the subject region with plausible
|
|
409
|
+
* background. Drop in a photo, get the subject erased.
|
|
410
|
+
* 2. **mask** — pass `file` + `mask` (any image, white = inpaint).
|
|
411
|
+
* 3. **bbox** — pass `file` + `bbox: { x, y, w, h }`. Rectangular region.
|
|
412
|
+
*
|
|
413
|
+
* `dilation` (0..32, default 8) expands the mask before LaMa runs to
|
|
414
|
+
* reduce ghost outlines from tight masks.
|
|
415
|
+
*
|
|
416
|
+
* @example Auto-erase subject
|
|
417
|
+
* const png = await client.inpaint({ file: "./photo.jpg" });
|
|
418
|
+
*
|
|
419
|
+
* @example Erase a rectangular region
|
|
420
|
+
* const png = await client.inpaint({ file: "./photo.jpg", bbox: { x: 100, y: 100, w: 300, h: 400 } });
|
|
421
|
+
*/
|
|
422
|
+
inpaint(input: InpaintInput): Promise<Buffer>;
|
|
423
|
+
/**
|
|
424
|
+
* Two-tone silhouette portrait — subject in one solid color, bg in another.
|
|
425
|
+
* Apple Music / Spotify avatar aesthetic. Reuses BiRefNet mask path; no new
|
|
426
|
+
* model load, fast.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* const png = await client.silhouette({
|
|
430
|
+
* file: "./portrait.jpg",
|
|
431
|
+
* subjectColor: "#1E2960",
|
|
432
|
+
* bgColor: "#F0857C",
|
|
433
|
+
* });
|
|
434
|
+
*/
|
|
435
|
+
silhouette(input: SilhouetteInput): Promise<Buffer>;
|
|
370
436
|
/**
|
|
371
437
|
* Remove the background from up to 10 remote image URLs in a single call.
|
|
372
438
|
*
|
|
@@ -379,4 +445,4 @@ declare class Knockout {
|
|
|
379
445
|
private request;
|
|
380
446
|
}
|
|
381
447
|
|
|
382
|
-
export { type BatchInput, type BatchResponse, type BatchResultItem, type BatchUrlInput, type ColorizeInput, type CompareInput, DEFAULT_BASE_URL, type EstimateInput, type EstimateResponse, type FaceRestoreInput, type HeadshotInput, type HealthResponse, Knockout, KnockoutError, type KnockoutOptions, type MaskInput, type OpaqueFormat, type OutlineInput, type OutputFormat, type PreviewInput, type RemoveInput, type RemoveUrlInput, type ReplaceBgInput, type ShadowInput, type SmartCropInput, type StatsDay, type StatsResponse, type StickerInput, type StudioShotInput, type UpscaleInput, Knockout as default };
|
|
448
|
+
export { type BatchInput, type BatchResponse, type BatchResultItem, type BatchUrlInput, type ColorizeInput, type CompareInput, DEFAULT_BASE_URL, type EstimateInput, type EstimateResponse, type FaceRestoreInput, type HeadshotInput, type HealthResponse, type InpaintBbox, type InpaintInput, Knockout, KnockoutError, type KnockoutOptions, type MaskInput, type OpaqueFormat, type OutlineInput, type OutputFormat, type PreviewInput, type RemoveInput, type RemoveUrlInput, type ReplaceBgInput, type ShadowInput, type SilhouetteInput, type SmartCropInput, type StatsDay, type StatsResponse, type StickerInput, type StudioShotInput, type UpscaleInput, Knockout as default };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { basename } from 'path';
|
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
var DEFAULT_BASE_URL = "https://useknockout--api.modal.run";
|
|
6
|
-
var SDK_VERSION = "0.
|
|
6
|
+
var SDK_VERSION = "0.3.0";
|
|
7
7
|
var KnockoutError = class _KnockoutError extends Error {
|
|
8
8
|
status;
|
|
9
9
|
code;
|
|
@@ -258,6 +258,7 @@ var Knockout = class {
|
|
|
258
258
|
if (input.aspect) form.append("aspect", input.aspect);
|
|
259
259
|
if (input.padding !== void 0) form.append("padding", String(input.padding));
|
|
260
260
|
if (input.shadow !== void 0) form.append("shadow", input.shadow ? "true" : "false");
|
|
261
|
+
if (input.transparent !== void 0) form.append("transparent", input.transparent ? "true" : "false");
|
|
261
262
|
form.append("format", format);
|
|
262
263
|
const res = await this.request("POST", "/studio-shot", { body: form });
|
|
263
264
|
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
@@ -379,6 +380,73 @@ var Knockout = class {
|
|
|
379
380
|
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
380
381
|
return Buffer.from(await res.arrayBuffer());
|
|
381
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* Inpaint a region of an image using LaMa (Apache-2.0). Three modes,
|
|
385
|
+
* auto-detected from what you pass:
|
|
386
|
+
*
|
|
387
|
+
* 1. **auto-subject** — pass only `file`. BiRefNet derives the subject
|
|
388
|
+
* mask, inverts it, and LaMa fills the subject region with plausible
|
|
389
|
+
* background. Drop in a photo, get the subject erased.
|
|
390
|
+
* 2. **mask** — pass `file` + `mask` (any image, white = inpaint).
|
|
391
|
+
* 3. **bbox** — pass `file` + `bbox: { x, y, w, h }`. Rectangular region.
|
|
392
|
+
*
|
|
393
|
+
* `dilation` (0..32, default 8) expands the mask before LaMa runs to
|
|
394
|
+
* reduce ghost outlines from tight masks.
|
|
395
|
+
*
|
|
396
|
+
* @example Auto-erase subject
|
|
397
|
+
* const png = await client.inpaint({ file: "./photo.jpg" });
|
|
398
|
+
*
|
|
399
|
+
* @example Erase a rectangular region
|
|
400
|
+
* const png = await client.inpaint({ file: "./photo.jpg", bbox: { x: 100, y: 100, w: 300, h: 400 } });
|
|
401
|
+
*/
|
|
402
|
+
async inpaint(input) {
|
|
403
|
+
const format = input.format ?? "png";
|
|
404
|
+
const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });
|
|
405
|
+
const form = new FormData();
|
|
406
|
+
form.append("file", blob, filename);
|
|
407
|
+
if (input.mask !== void 0) {
|
|
408
|
+
const { blob: maskBlob, filename: maskFilename } = await toBlob({
|
|
409
|
+
file: input.mask,
|
|
410
|
+
filename: input.maskFilename ?? "mask.png"
|
|
411
|
+
});
|
|
412
|
+
form.append("mask", maskBlob, maskFilename);
|
|
413
|
+
}
|
|
414
|
+
if (input.bbox) {
|
|
415
|
+
form.append("x", String(input.bbox.x));
|
|
416
|
+
form.append("y", String(input.bbox.y));
|
|
417
|
+
form.append("w", String(input.bbox.w));
|
|
418
|
+
form.append("h", String(input.bbox.h));
|
|
419
|
+
}
|
|
420
|
+
if (input.dilation !== void 0) form.append("dilation", String(input.dilation));
|
|
421
|
+
form.append("format", format);
|
|
422
|
+
const res = await this.request("POST", "/inpaint", { body: form });
|
|
423
|
+
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
424
|
+
return Buffer.from(await res.arrayBuffer());
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Two-tone silhouette portrait — subject in one solid color, bg in another.
|
|
428
|
+
* Apple Music / Spotify avatar aesthetic. Reuses BiRefNet mask path; no new
|
|
429
|
+
* model load, fast.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* const png = await client.silhouette({
|
|
433
|
+
* file: "./portrait.jpg",
|
|
434
|
+
* subjectColor: "#1E2960",
|
|
435
|
+
* bgColor: "#F0857C",
|
|
436
|
+
* });
|
|
437
|
+
*/
|
|
438
|
+
async silhouette(input) {
|
|
439
|
+
const format = input.format ?? "png";
|
|
440
|
+
const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });
|
|
441
|
+
const form = new FormData();
|
|
442
|
+
form.append("file", blob, filename);
|
|
443
|
+
if (input.subjectColor) form.append("subject_color", input.subjectColor);
|
|
444
|
+
if (input.bgColor) form.append("bg_color", input.bgColor);
|
|
445
|
+
form.append("format", format);
|
|
446
|
+
const res = await this.request("POST", "/silhouette", { body: form });
|
|
447
|
+
if (!res.ok) throw new KnockoutError(res.status, await res.text());
|
|
448
|
+
return Buffer.from(await res.arrayBuffer());
|
|
449
|
+
}
|
|
382
450
|
/**
|
|
383
451
|
* Remove the background from up to 10 remote image URLs in a single call.
|
|
384
452
|
*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * @useknockout/node — official TypeScript / Node.js client for the useknockout API.\r\n *\r\n * Quick start:\r\n *\r\n * import { Knockout } from \"@useknockout/node\";\r\n *\r\n * const client = new Knockout({ token: process.env.KNOCKOUT_TOKEN! });\r\n * const png = await client.remove({ file: \"./input.jpg\" }); // Buffer of PNG bytes\r\n * await writeFile(\"out.png\", png);\r\n */\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://useknockout--api.modal.run\";\r\nconst SDK_VERSION = \"0.1.0\";\r\n\r\nexport type OutputFormat = \"png\" | \"webp\";\r\nexport type OpaqueFormat = \"png\" | \"webp\" | \"jpg\";\r\n\r\ntype FileInput = string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n\r\nexport interface KnockoutOptions {\r\n /** API bearer token. Required unless your self-hosted instance has no auth. */\r\n token?: string;\r\n /** Override the API base URL. Defaults to the hosted endpoint. */\r\n baseUrl?: string;\r\n /** Per-request timeout in milliseconds. Default 60_000. */\r\n timeoutMs?: number;\r\n /** Custom fetch (useful for edge runtimes / polyfills). Defaults to global fetch. */\r\n fetch?: typeof fetch;\r\n}\r\n\r\nexport interface RemoveInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface RemoveUrlInput {\r\n /** Remote URL of the image to process. */\r\n url: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface ReplaceBgInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the foreground image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Hex color for the new background. Default \"#FFFFFF\". Ignored if `bgUrl` is set. */\r\n bgColor?: string;\r\n /** Remote URL of an image to use as the new background. Takes precedence over `bgColor`. */\r\n bgUrl?: string;\r\n /** Output format. \"jpg\" is smallest. Default \"png\". */\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface BatchInput {\r\n /** Array of local paths / buffers / blobs. Up to 10. */\r\n files: Array<string | Buffer | Blob | ArrayBuffer | Uint8Array>;\r\n /** Optional filenames aligned to `files`. */\r\n filenames?: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchUrlInput {\r\n /** Remote URLs to process. Up to 10. */\r\n urls: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchResultItem {\r\n filename?: string;\r\n url?: string;\r\n success: boolean;\r\n format?: OutputFormat;\r\n size_bytes?: number;\r\n data_base64?: string;\r\n error?: string;\r\n}\r\n\r\nexport interface BatchResponse {\r\n count: number;\r\n format: OutputFormat;\r\n results: BatchResultItem[];\r\n}\r\n\r\nexport interface MaskInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface SmartCropInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Padding around the subject bbox, in pixels. Default 24. */\r\n padding?: number;\r\n /** Return transparent cutout (true) or cropped region from original (false). Default true. */\r\n transparent?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ShadowInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n bgUrl?: string;\r\n shadowColor?: string;\r\n shadowOffsetX?: number;\r\n shadowOffsetY?: number;\r\n shadowBlur?: number;\r\n shadowOpacity?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface StickerInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#FFFFFF\". */\r\n strokeColor?: string;\r\n /** Outline width in pixels. Default 20. */\r\n strokeWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface OutlineInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#000000\". */\r\n outlineColor?: string;\r\n /** Outline width in pixels. Default 4. */\r\n outlineWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface StudioShotInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n /** e.g. \"1:1\", \"4:5\", \"16:9\". Default \"1:1\". */\r\n aspect?: string;\r\n padding?: number;\r\n shadow?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface CompareInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface HeadshotInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the background. Default \"#FFFFFF\". Ignored if `bgBlur` is true. */\r\n bgColor?: string;\r\n /** Use a blurred copy of the original image as the background. Default false. */\r\n bgBlur?: boolean;\r\n /** Gaussian blur radius for the background when `bgBlur` is true. Default 20. */\r\n blurRadius?: number;\r\n /** Output aspect \"W:H\". Default \"4:5\" (portrait). */\r\n aspect?: string;\r\n /** Padding around the subject bbox, in pixels. Default 64. */\r\n padding?: number;\r\n /** Vertical headroom as a ratio of canvas height (0–0.5). Default 0.18. */\r\n headTopRatio?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface PreviewInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Long-edge cap in pixels (64–1024). Default 512. */\r\n maxDim?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface UpscaleInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Upscale factor: 2 or 4. Default 4. */\r\n scale?: 2 | 4;\r\n /**\r\n * Backend. `swin2sr` (default, v0.6.0+) is sharper on real photos.\r\n * `realesrgan` is the legacy backend — better on anime/illustrations.\r\n */\r\n model?: \"swin2sr\" | \"realesrgan\";\r\n /** Route through GFPGAN to fix facial detail. Slower, use for portraits. Implies realesrgan. */\r\n faceEnhance?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface FaceRestoreInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Restore only the most prominent face (faster). Default false (all faces). */\r\n onlyCenterFace?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ColorizeInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface EstimateInput {\r\n /** Endpoint name without leading slash, e.g. \"remove\" or \"headshot\". */\r\n endpoint: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface EstimateResponse {\r\n endpoint: string;\r\n image_pixels: number;\r\n est_latency_ms_warm: number;\r\n est_latency_ms_cold: number;\r\n est_cost_usd: number;\r\n free_during_beta: boolean;\r\n note: string;\r\n}\r\n\r\nexport interface StatsDay {\r\n date: string;\r\n count: number;\r\n}\r\n\r\nexport interface StatsResponse {\r\n total_processed: number;\r\n today: number;\r\n last_7_days: StatsDay[];\r\n error?: string;\r\n detail?: string;\r\n}\r\n\r\nexport interface HealthResponse {\r\n status: string;\r\n model: string;\r\n}\r\n\r\n/**\r\n * Error thrown when the API returns a non-2xx response.\r\n */\r\nexport class KnockoutError extends Error {\r\n public readonly status: number;\r\n public readonly code: \"auth\" | \"rate_limit\" | \"bad_request\" | \"payload_too_large\" | \"server\" | \"unknown\";\r\n public readonly body: string;\r\n\r\n constructor(status: number, body: string) {\r\n const code = KnockoutError.classify(status);\r\n super(`Knockout API error ${status} (${code}): ${body || \"no body\"}`);\r\n this.name = \"KnockoutError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n }\r\n\r\n private static classify(status: number): KnockoutError[\"code\"] {\r\n if (status === 401 || status === 403) return \"auth\";\r\n if (status === 429) return \"rate_limit\";\r\n if (status === 413) return \"payload_too_large\";\r\n if (status >= 400 && status < 500) return \"bad_request\";\r\n if (status >= 500) return \"server\";\r\n return \"unknown\";\r\n }\r\n}\r\n\r\n/**\r\n * useknockout API client.\r\n *\r\n * All methods return a `Buffer` (Node) of the processed image bytes.\r\n * Use `.toString(\"base64\")` or `writeFile(path, buf)` to persist.\r\n */\r\nexport class Knockout {\r\n private readonly baseUrl: string;\r\n private readonly token: string | undefined;\r\n private readonly timeoutMs: number;\r\n private readonly fetchImpl: typeof fetch;\r\n\r\n constructor(options: KnockoutOptions = {}) {\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\r\n this.token = options.token;\r\n this.timeoutMs = options.timeoutMs ?? 60_000;\r\n const fetchRef = options.fetch ?? globalThis.fetch;\r\n if (!fetchRef) {\r\n throw new Error(\r\n \"Global fetch is unavailable. Provide `options.fetch` or use Node 18+.\"\r\n );\r\n }\r\n this.fetchImpl = fetchRef.bind(globalThis);\r\n }\r\n\r\n /** Hit GET /health — no auth required. */\r\n async health(): Promise<HealthResponse> {\r\n const res = await this.request(\"GET\", \"/health\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as HealthResponse;\r\n }\r\n\r\n /**\r\n * Public usage counter — total images processed all-time, today, and a 7-day breakdown.\r\n * Use for landing-page social proof. Eventually consistent across containers.\r\n */\r\n async stats(): Promise<StatsResponse> {\r\n const res = await this.request(\"GET\", \"/stats\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as StatsResponse;\r\n }\r\n\r\n /**\r\n * Predict latency + cost for an endpoint and image size without doing any GPU work.\r\n *\r\n * @example\r\n * const est = await client.estimate({ endpoint: \"remove\", width: 1024, height: 1024 });\r\n */\r\n async estimate(input: EstimateInput): Promise<EstimateResponse> {\r\n const res = await this.request(\"POST\", \"/estimate\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n endpoint: input.endpoint,\r\n width: input.width,\r\n height: input.height,\r\n }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as EstimateResponse;\r\n }\r\n\r\n /**\r\n * Remove the background from an image, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.remove({ file: \"./input.jpg\" });\r\n */\r\n async remove(input: RemoveInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob(input);\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const res = await this.request(\"POST\", `/remove?format=${format}`, {\r\n body: form,\r\n });\r\n\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from a remote URL, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.removeUrl({ url: \"https://example.com/cat.jpg\" });\r\n */\r\n async removeUrl(input: RemoveUrlInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const res = await this.request(\"POST\", \"/remove-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ url: input.url, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Replace the background with a solid color or a remote image.\r\n *\r\n * @example Solid color\r\n * const jpg = await client.replaceBackground({ file: \"./cat.jpg\", bgColor: \"#FF5733\", format: \"jpg\" });\r\n *\r\n * @example Remote image as new background\r\n * const png = await client.replaceBackground({\r\n * file: \"./cat.jpg\",\r\n * bgUrl: \"https://example.com/beach.jpg\",\r\n * });\r\n */\r\n async replaceBackground(input: ReplaceBgInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const params = new URLSearchParams({ format });\r\n if (input.bgUrl) params.set(\"bg_url\", input.bgUrl);\r\n if (input.bgColor) params.set(\"bg_color\", input.bgColor);\r\n\r\n const res = await this.request(\"POST\", `/replace-bg?${params.toString()}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 images in a single call.\r\n * Returns a JSON object with base64-encoded result bytes per image.\r\n *\r\n * @example\r\n * const batch = await client.removeBatch({\r\n * files: [\"./a.jpg\", \"./b.jpg\", \"./c.jpg\"],\r\n * format: \"png\",\r\n * });\r\n * for (const r of batch.results) {\r\n * if (r.success) await writeFile(r.filename!, Buffer.from(r.data_base64!, \"base64\"));\r\n * }\r\n */\r\n async removeBatch(input: BatchInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.files.length === 0) throw new Error(\"At least one file required\");\r\n if (input.files.length > 10) throw new Error(\"Max 10 files per batch\");\r\n\r\n const form = new FormData();\r\n for (let i = 0; i < input.files.length; i++) {\r\n const name = input.filenames?.[i];\r\n const { blob, filename } = await toBlob({ file: input.files[i]!, filename: name });\r\n form.append(\"files\", blob, filename);\r\n }\r\n\r\n const res = await this.request(\"POST\", `/remove-batch?format=${format}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n /**\r\n * Return only the alpha mask as a grayscale PNG/WebP.\r\n * Useful when chaining into your own compositing pipeline.\r\n */\r\n async mask(input: MaskInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/mask\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Auto-crop to the subject's tight bounding box with configurable padding.\r\n * Returns either a transparent cutout or a cropped region from the original image.\r\n */\r\n async smartCrop(input: SmartCropInput): Promise<Buffer> {\r\n const transparent = input.transparent ?? true;\r\n const format: OpaqueFormat =\r\n (input.format as OpaqueFormat | undefined) ?? (transparent ? \"png\" : \"jpg\");\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"padding\", String(input.padding ?? 24));\r\n form.append(\"transparent\", transparent ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/smart-crop\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Composite subject onto a new background with a configurable drop shadow.\r\n */\r\n async shadow(input: ShadowInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgUrl) form.append(\"bg_url\", input.bgUrl);\r\n if (input.shadowColor) form.append(\"shadow_color\", input.shadowColor);\r\n if (input.shadowOffsetX !== undefined) form.append(\"shadow_offset_x\", String(input.shadowOffsetX));\r\n if (input.shadowOffsetY !== undefined) form.append(\"shadow_offset_y\", String(input.shadowOffsetY));\r\n if (input.shadowBlur !== undefined) form.append(\"shadow_blur\", String(input.shadowBlur));\r\n if (input.shadowOpacity !== undefined) form.append(\"shadow_opacity\", String(input.shadowOpacity));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/shadow\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Sticker style — subject with a thick outline on transparent background.\r\n * Perfect for WhatsApp / iMessage / Telegram stickers.\r\n */\r\n async sticker(input: StickerInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.strokeColor) form.append(\"stroke_color\", input.strokeColor);\r\n if (input.strokeWidth !== undefined) form.append(\"stroke_width\", String(input.strokeWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/sticker\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Subject on transparent background with a thin configurable outline.\r\n */\r\n async outline(input: OutlineInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.outlineColor) form.append(\"outline_color\", input.outlineColor);\r\n if (input.outlineWidth !== undefined) form.append(\"outline_width\", String(input.outlineWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/outline\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * E-commerce preset — cutout + tight crop + centered + optional shadow on a standard aspect canvas.\r\n */\r\n async studioShot(input: StudioShotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.shadow !== undefined) form.append(\"shadow\", input.shadow ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/studio-shot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Before/after side-by-side preview — original on left, transparent cutout (on a checkerboard) on right.\r\n */\r\n async compare(input: CompareInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/compare\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * LinkedIn-ready headshot preset — bg removal + portrait crop + center face + bg color or blur.\r\n *\r\n * @example Solid bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgColor: \"#0A0A0A\" });\r\n *\r\n * @example Blurred original as bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgBlur: true, blurRadius: 24 });\r\n */\r\n async headshot(input: HeadshotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgBlur !== undefined) form.append(\"bg_blur\", input.bgBlur ? \"true\" : \"false\");\r\n if (input.blurRadius !== undefined) form.append(\"blur_radius\", String(input.blurRadius));\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.headTopRatio !== undefined) form.append(\"head_top_ratio\", String(input.headTopRatio));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/headshot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Fast low-res preview cutout (~80ms warm). Skips pymatting refinement and downscales\r\n * input to `maxDim` (default 512px on the long edge). Use for UX progress indicators.\r\n */\r\n async preview(input: PreviewInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"max_dim\", String(input.maxDim ?? 512));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/preview\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * 2x / 4x super-resolution. Defaults to Swin2SR (SwinV2 transformer) for sharper\r\n * detail on real photos. Pass `model: \"realesrgan\"` for the legacy backend\r\n * (better on anime / illustrations). `faceEnhance: true` routes portraits\r\n * through GFPGAN.\r\n *\r\n * @example Cutout → 4x upscale (print-ready)\r\n * const png = await client.remove({ file: \"./photo.jpg\" });\r\n * const big = await client.upscale({ file: png, scale: 4 });\r\n */\r\n async upscale(input: UpscaleInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"scale\", String(input.scale ?? 4));\r\n form.append(\"model\", input.model ?? \"swin2sr\");\r\n if (input.faceEnhance !== undefined) {\r\n form.append(\"face_enhance\", input.faceEnhance ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/upscale\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * GFPGAN v1.4 portrait restoration — fix blurry / damaged / low-res faces.\r\n * Background is upscaled 2x by Real-ESRGAN. Pairs with /headshot.\r\n */\r\n async faceRestore(input: FaceRestoreInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.onlyCenterFace !== undefined) {\r\n form.append(\"only_center_face\", input.onlyCenterFace ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/face-restore\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Colorize a black-and-white or grayscale photo (DDColor v0.7.0+).\r\n *\r\n * Apache-2.0 model, single feed-forward (no diffusion), ~500ms warm on L4.\r\n * Works on any input — color photos are converted to grayscale internally\r\n * before color is predicted, which makes round-trip recoloring easy too.\r\n *\r\n * @example\r\n * const buf = await client.colorize({ file: \"./old-photo.jpg\" });\r\n * await writeFile(\"colorized.png\", buf);\r\n */\r\n async colorize(input: ColorizeInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/colorize\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 remote image URLs in a single call.\r\n *\r\n * @example\r\n * const batch = await client.removeBatchUrl({\r\n * urls: [\"https://a.jpg\", \"https://b.jpg\"],\r\n * });\r\n */\r\n async removeBatchUrl(input: BatchUrlInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.urls.length === 0) throw new Error(\"At least one URL required\");\r\n if (input.urls.length > 10) throw new Error(\"Max 10 URLs per batch\");\r\n\r\n const res = await this.request(\"POST\", \"/remove-batch-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ urls: input.urls, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n private async request(\r\n method: \"GET\" | \"POST\",\r\n path: string,\r\n init: { headers?: Record<string, string>; body?: BodyInit } = {}\r\n ): Promise<Response> {\r\n const url = `${this.baseUrl}${path}`;\r\n const headers: Record<string, string> = {\r\n \"User-Agent\": `useknockout-node/${SDK_VERSION}`,\r\n ...(init.headers ?? {}),\r\n };\r\n if (this.token) headers[\"Authorization\"] = `Bearer ${this.token}`;\r\n\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n try {\r\n return await this.fetchImpl(url, {\r\n method,\r\n headers,\r\n body: init.body,\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n\r\nasync function toBlob(\r\n input: { file: string | Buffer | Blob | ArrayBuffer | Uint8Array; filename?: string }\r\n): Promise<{ blob: Blob; filename: string }> {\r\n const { file } = input;\r\n const filename = input.filename ?? inferFilename(file);\r\n\r\n if (typeof file === \"string\") {\r\n const data = await readFile(file);\r\n return { blob: new Blob([new Uint8Array(data)]), filename };\r\n }\r\n if (file instanceof Blob) {\r\n return { blob: file, filename };\r\n }\r\n if (file instanceof ArrayBuffer) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (file instanceof Uint8Array) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (Buffer.isBuffer(file)) {\r\n return {\r\n blob: new Blob([new Uint8Array(file)]),\r\n filename,\r\n };\r\n }\r\n throw new TypeError(\"Unsupported `file` input. Provide a path, Buffer, Blob, ArrayBuffer, or Uint8Array.\");\r\n}\r\n\r\nfunction inferFilename(file: string | Buffer | Blob | ArrayBuffer | Uint8Array): string {\r\n if (typeof file === \"string\") return basename(file) || \"image\";\r\n return \"image\";\r\n}\r\n\r\nexport default Knockout;\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * @useknockout/node — official TypeScript / Node.js client for the useknockout API.\r\n *\r\n * Quick start:\r\n *\r\n * import { Knockout } from \"@useknockout/node\";\r\n *\r\n * const client = new Knockout({ token: process.env.KNOCKOUT_TOKEN! });\r\n * const png = await client.remove({ file: \"./input.jpg\" }); // Buffer of PNG bytes\r\n * await writeFile(\"out.png\", png);\r\n */\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://useknockout--api.modal.run\";\r\nconst SDK_VERSION = \"0.3.0\";\r\n\r\nexport type OutputFormat = \"png\" | \"webp\";\r\nexport type OpaqueFormat = \"png\" | \"webp\" | \"jpg\";\r\n\r\ntype FileInput = string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n\r\nexport interface KnockoutOptions {\r\n /** API bearer token. Required unless your self-hosted instance has no auth. */\r\n token?: string;\r\n /** Override the API base URL. Defaults to the hosted endpoint. */\r\n baseUrl?: string;\r\n /** Per-request timeout in milliseconds. Default 60_000. */\r\n timeoutMs?: number;\r\n /** Custom fetch (useful for edge runtimes / polyfills). Defaults to global fetch. */\r\n fetch?: typeof fetch;\r\n}\r\n\r\nexport interface RemoveInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface RemoveUrlInput {\r\n /** Remote URL of the image to process. */\r\n url: string;\r\n /** Output format. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface ReplaceBgInput {\r\n /** Local file path, Buffer, Blob, or ArrayBuffer of the foreground image. */\r\n file: string | Buffer | Blob | ArrayBuffer | Uint8Array;\r\n /** Optional filename — inferred from path when `file` is a string. */\r\n filename?: string;\r\n /** Hex color for the new background. Default \"#FFFFFF\". Ignored if `bgUrl` is set. */\r\n bgColor?: string;\r\n /** Remote URL of an image to use as the new background. Takes precedence over `bgColor`. */\r\n bgUrl?: string;\r\n /** Output format. \"jpg\" is smallest. Default \"png\". */\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface BatchInput {\r\n /** Array of local paths / buffers / blobs. Up to 10. */\r\n files: Array<string | Buffer | Blob | ArrayBuffer | Uint8Array>;\r\n /** Optional filenames aligned to `files`. */\r\n filenames?: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchUrlInput {\r\n /** Remote URLs to process. Up to 10. */\r\n urls: string[];\r\n /** Output format for each image. Default \"png\". */\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface BatchResultItem {\r\n filename?: string;\r\n url?: string;\r\n success: boolean;\r\n format?: OutputFormat;\r\n size_bytes?: number;\r\n data_base64?: string;\r\n error?: string;\r\n}\r\n\r\nexport interface BatchResponse {\r\n count: number;\r\n format: OutputFormat;\r\n results: BatchResultItem[];\r\n}\r\n\r\nexport interface MaskInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface SmartCropInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Padding around the subject bbox, in pixels. Default 24. */\r\n padding?: number;\r\n /** Return transparent cutout (true) or cropped region from original (false). Default true. */\r\n transparent?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ShadowInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n bgUrl?: string;\r\n shadowColor?: string;\r\n shadowOffsetX?: number;\r\n shadowOffsetY?: number;\r\n shadowBlur?: number;\r\n shadowOpacity?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface StickerInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#FFFFFF\". */\r\n strokeColor?: string;\r\n /** Outline width in pixels. Default 20. */\r\n strokeWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface OutlineInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the outline. Default \"#000000\". */\r\n outlineColor?: string;\r\n /** Outline width in pixels. Default 4. */\r\n outlineWidth?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface StudioShotInput {\r\n file: FileInput;\r\n filename?: string;\r\n bgColor?: string;\r\n /** e.g. \"1:1\", \"4:5\", \"16:9\". Default \"1:1\". */\r\n aspect?: string;\r\n padding?: number;\r\n shadow?: boolean;\r\n /** Keep a transparent background. Ignores bgColor and shadow; output is PNG (jpg is coerced). */\r\n transparent?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface CompareInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface HeadshotInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the background. Default \"#FFFFFF\". Ignored if `bgBlur` is true. */\r\n bgColor?: string;\r\n /** Use a blurred copy of the original image as the background. Default false. */\r\n bgBlur?: boolean;\r\n /** Gaussian blur radius for the background when `bgBlur` is true. Default 20. */\r\n blurRadius?: number;\r\n /** Output aspect \"W:H\". Default \"4:5\" (portrait). */\r\n aspect?: string;\r\n /** Padding around the subject bbox, in pixels. Default 64. */\r\n padding?: number;\r\n /** Vertical headroom as a ratio of canvas height (0–0.5). Default 0.18. */\r\n headTopRatio?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface PreviewInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Long-edge cap in pixels (64–1024). Default 512. */\r\n maxDim?: number;\r\n format?: OutputFormat;\r\n}\r\n\r\nexport interface UpscaleInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Upscale factor: 2 or 4. Default 4. */\r\n scale?: 2 | 4;\r\n /**\r\n * Backend. `swin2sr` (default, v0.6.0+) is sharper on real photos.\r\n * `realesrgan` is the legacy backend — better on anime/illustrations.\r\n */\r\n model?: \"swin2sr\" | \"realesrgan\";\r\n /** Route through GFPGAN to fix facial detail. Slower, use for portraits. Implies realesrgan. */\r\n faceEnhance?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface FaceRestoreInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Restore only the most prominent face (faster). Default false (all faces). */\r\n onlyCenterFace?: boolean;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface ColorizeInput {\r\n file: FileInput;\r\n filename?: string;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface SilhouetteInput {\r\n file: FileInput;\r\n filename?: string;\r\n /** Hex color for the subject silhouette. Default \"#7C3AED\" (purple). */\r\n subjectColor?: string;\r\n /** Hex color for the background. Default \"#FFFFFF\" (white). */\r\n bgColor?: string;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface InpaintBbox {\r\n x: number;\r\n y: number;\r\n w: number;\r\n h: number;\r\n}\r\n\r\nexport interface InpaintInput {\r\n file: FileInput;\r\n filename?: string;\r\n /**\r\n * Optional mask. White pixels = inpaint, black = keep. If omitted (and no\r\n * `bbox`), auto-subject mode runs BiRefNet on the input and inverts the\r\n * subject mask.\r\n */\r\n mask?: FileInput;\r\n maskFilename?: string;\r\n /** Rectangular region to inpaint. Mutually exclusive with `mask`. */\r\n bbox?: InpaintBbox;\r\n /** Mask dilation in pixels. Default 8, range 0..32. */\r\n dilation?: number;\r\n format?: OpaqueFormat;\r\n}\r\n\r\nexport interface EstimateInput {\r\n /** Endpoint name without leading slash, e.g. \"remove\" or \"headshot\". */\r\n endpoint: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface EstimateResponse {\r\n endpoint: string;\r\n image_pixels: number;\r\n est_latency_ms_warm: number;\r\n est_latency_ms_cold: number;\r\n est_cost_usd: number;\r\n free_during_beta: boolean;\r\n note: string;\r\n}\r\n\r\nexport interface StatsDay {\r\n date: string;\r\n count: number;\r\n}\r\n\r\nexport interface StatsResponse {\r\n total_processed: number;\r\n today: number;\r\n last_7_days: StatsDay[];\r\n error?: string;\r\n detail?: string;\r\n}\r\n\r\nexport interface HealthResponse {\r\n status: string;\r\n model: string;\r\n}\r\n\r\n/**\r\n * Error thrown when the API returns a non-2xx response.\r\n */\r\nexport class KnockoutError extends Error {\r\n public readonly status: number;\r\n public readonly code: \"auth\" | \"rate_limit\" | \"bad_request\" | \"payload_too_large\" | \"server\" | \"unknown\";\r\n public readonly body: string;\r\n\r\n constructor(status: number, body: string) {\r\n const code = KnockoutError.classify(status);\r\n super(`Knockout API error ${status} (${code}): ${body || \"no body\"}`);\r\n this.name = \"KnockoutError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n }\r\n\r\n private static classify(status: number): KnockoutError[\"code\"] {\r\n if (status === 401 || status === 403) return \"auth\";\r\n if (status === 429) return \"rate_limit\";\r\n if (status === 413) return \"payload_too_large\";\r\n if (status >= 400 && status < 500) return \"bad_request\";\r\n if (status >= 500) return \"server\";\r\n return \"unknown\";\r\n }\r\n}\r\n\r\n/**\r\n * useknockout API client.\r\n *\r\n * All methods return a `Buffer` (Node) of the processed image bytes.\r\n * Use `.toString(\"base64\")` or `writeFile(path, buf)` to persist.\r\n */\r\nexport class Knockout {\r\n private readonly baseUrl: string;\r\n private readonly token: string | undefined;\r\n private readonly timeoutMs: number;\r\n private readonly fetchImpl: typeof fetch;\r\n\r\n constructor(options: KnockoutOptions = {}) {\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\r\n this.token = options.token;\r\n this.timeoutMs = options.timeoutMs ?? 60_000;\r\n const fetchRef = options.fetch ?? globalThis.fetch;\r\n if (!fetchRef) {\r\n throw new Error(\r\n \"Global fetch is unavailable. Provide `options.fetch` or use Node 18+.\"\r\n );\r\n }\r\n this.fetchImpl = fetchRef.bind(globalThis);\r\n }\r\n\r\n /** Hit GET /health — no auth required. */\r\n async health(): Promise<HealthResponse> {\r\n const res = await this.request(\"GET\", \"/health\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as HealthResponse;\r\n }\r\n\r\n /**\r\n * Public usage counter — total images processed all-time, today, and a 7-day breakdown.\r\n * Use for landing-page social proof. Eventually consistent across containers.\r\n */\r\n async stats(): Promise<StatsResponse> {\r\n const res = await this.request(\"GET\", \"/stats\");\r\n const body = await res.text();\r\n if (!res.ok) throw new KnockoutError(res.status, body);\r\n return JSON.parse(body) as StatsResponse;\r\n }\r\n\r\n /**\r\n * Predict latency + cost for an endpoint and image size without doing any GPU work.\r\n *\r\n * @example\r\n * const est = await client.estimate({ endpoint: \"remove\", width: 1024, height: 1024 });\r\n */\r\n async estimate(input: EstimateInput): Promise<EstimateResponse> {\r\n const res = await this.request(\"POST\", \"/estimate\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n endpoint: input.endpoint,\r\n width: input.width,\r\n height: input.height,\r\n }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as EstimateResponse;\r\n }\r\n\r\n /**\r\n * Remove the background from an image, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.remove({ file: \"./input.jpg\" });\r\n */\r\n async remove(input: RemoveInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob(input);\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const res = await this.request(\"POST\", `/remove?format=${format}`, {\r\n body: form,\r\n });\r\n\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from a remote URL, returning the cleaned PNG/WebP bytes.\r\n *\r\n * @example\r\n * const png = await client.removeUrl({ url: \"https://example.com/cat.jpg\" });\r\n */\r\n async removeUrl(input: RemoveUrlInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const res = await this.request(\"POST\", \"/remove-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ url: input.url, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Replace the background with a solid color or a remote image.\r\n *\r\n * @example Solid color\r\n * const jpg = await client.replaceBackground({ file: \"./cat.jpg\", bgColor: \"#FF5733\", format: \"jpg\" });\r\n *\r\n * @example Remote image as new background\r\n * const png = await client.replaceBackground({\r\n * file: \"./cat.jpg\",\r\n * bgUrl: \"https://example.com/beach.jpg\",\r\n * });\r\n */\r\n async replaceBackground(input: ReplaceBgInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n\r\n const params = new URLSearchParams({ format });\r\n if (input.bgUrl) params.set(\"bg_url\", input.bgUrl);\r\n if (input.bgColor) params.set(\"bg_color\", input.bgColor);\r\n\r\n const res = await this.request(\"POST\", `/replace-bg?${params.toString()}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 images in a single call.\r\n * Returns a JSON object with base64-encoded result bytes per image.\r\n *\r\n * @example\r\n * const batch = await client.removeBatch({\r\n * files: [\"./a.jpg\", \"./b.jpg\", \"./c.jpg\"],\r\n * format: \"png\",\r\n * });\r\n * for (const r of batch.results) {\r\n * if (r.success) await writeFile(r.filename!, Buffer.from(r.data_base64!, \"base64\"));\r\n * }\r\n */\r\n async removeBatch(input: BatchInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.files.length === 0) throw new Error(\"At least one file required\");\r\n if (input.files.length > 10) throw new Error(\"Max 10 files per batch\");\r\n\r\n const form = new FormData();\r\n for (let i = 0; i < input.files.length; i++) {\r\n const name = input.filenames?.[i];\r\n const { blob, filename } = await toBlob({ file: input.files[i]!, filename: name });\r\n form.append(\"files\", blob, filename);\r\n }\r\n\r\n const res = await this.request(\"POST\", `/remove-batch?format=${format}`, {\r\n body: form,\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n /**\r\n * Return only the alpha mask as a grayscale PNG/WebP.\r\n * Useful when chaining into your own compositing pipeline.\r\n */\r\n async mask(input: MaskInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/mask\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Auto-crop to the subject's tight bounding box with configurable padding.\r\n * Returns either a transparent cutout or a cropped region from the original image.\r\n */\r\n async smartCrop(input: SmartCropInput): Promise<Buffer> {\r\n const transparent = input.transparent ?? true;\r\n const format: OpaqueFormat =\r\n (input.format as OpaqueFormat | undefined) ?? (transparent ? \"png\" : \"jpg\");\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"padding\", String(input.padding ?? 24));\r\n form.append(\"transparent\", transparent ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/smart-crop\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Composite subject onto a new background with a configurable drop shadow.\r\n */\r\n async shadow(input: ShadowInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgUrl) form.append(\"bg_url\", input.bgUrl);\r\n if (input.shadowColor) form.append(\"shadow_color\", input.shadowColor);\r\n if (input.shadowOffsetX !== undefined) form.append(\"shadow_offset_x\", String(input.shadowOffsetX));\r\n if (input.shadowOffsetY !== undefined) form.append(\"shadow_offset_y\", String(input.shadowOffsetY));\r\n if (input.shadowBlur !== undefined) form.append(\"shadow_blur\", String(input.shadowBlur));\r\n if (input.shadowOpacity !== undefined) form.append(\"shadow_opacity\", String(input.shadowOpacity));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/shadow\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Sticker style — subject with a thick outline on transparent background.\r\n * Perfect for WhatsApp / iMessage / Telegram stickers.\r\n */\r\n async sticker(input: StickerInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.strokeColor) form.append(\"stroke_color\", input.strokeColor);\r\n if (input.strokeWidth !== undefined) form.append(\"stroke_width\", String(input.strokeWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/sticker\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Subject on transparent background with a thin configurable outline.\r\n */\r\n async outline(input: OutlineInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.outlineColor) form.append(\"outline_color\", input.outlineColor);\r\n if (input.outlineWidth !== undefined) form.append(\"outline_width\", String(input.outlineWidth));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/outline\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * E-commerce preset — cutout + tight crop + centered + optional shadow on a standard aspect canvas.\r\n */\r\n async studioShot(input: StudioShotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.shadow !== undefined) form.append(\"shadow\", input.shadow ? \"true\" : \"false\");\r\n if (input.transparent !== undefined) form.append(\"transparent\", input.transparent ? \"true\" : \"false\");\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/studio-shot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Before/after side-by-side preview — original on left, transparent cutout (on a checkerboard) on right.\r\n */\r\n async compare(input: CompareInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/compare\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * LinkedIn-ready headshot preset — bg removal + portrait crop + center face + bg color or blur.\r\n *\r\n * @example Solid bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgColor: \"#0A0A0A\" });\r\n *\r\n * @example Blurred original as bg\r\n * const jpg = await client.headshot({ file: \"./photo.jpg\", bgBlur: true, blurRadius: 24 });\r\n */\r\n async headshot(input: HeadshotInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"jpg\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n if (input.bgBlur !== undefined) form.append(\"bg_blur\", input.bgBlur ? \"true\" : \"false\");\r\n if (input.blurRadius !== undefined) form.append(\"blur_radius\", String(input.blurRadius));\r\n if (input.aspect) form.append(\"aspect\", input.aspect);\r\n if (input.padding !== undefined) form.append(\"padding\", String(input.padding));\r\n if (input.headTopRatio !== undefined) form.append(\"head_top_ratio\", String(input.headTopRatio));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/headshot\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Fast low-res preview cutout (~80ms warm). Skips pymatting refinement and downscales\r\n * input to `maxDim` (default 512px on the long edge). Use for UX progress indicators.\r\n */\r\n async preview(input: PreviewInput): Promise<Buffer> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"max_dim\", String(input.maxDim ?? 512));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/preview\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * 2x / 4x super-resolution. Defaults to Swin2SR (SwinV2 transformer) for sharper\r\n * detail on real photos. Pass `model: \"realesrgan\"` for the legacy backend\r\n * (better on anime / illustrations). `faceEnhance: true` routes portraits\r\n * through GFPGAN.\r\n *\r\n * @example Cutout → 4x upscale (print-ready)\r\n * const png = await client.remove({ file: \"./photo.jpg\" });\r\n * const big = await client.upscale({ file: png, scale: 4 });\r\n */\r\n async upscale(input: UpscaleInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"scale\", String(input.scale ?? 4));\r\n form.append(\"model\", input.model ?? \"swin2sr\");\r\n if (input.faceEnhance !== undefined) {\r\n form.append(\"face_enhance\", input.faceEnhance ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/upscale\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * GFPGAN v1.4 portrait restoration — fix blurry / damaged / low-res faces.\r\n * Background is upscaled 2x by Real-ESRGAN. Pairs with /headshot.\r\n */\r\n async faceRestore(input: FaceRestoreInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.onlyCenterFace !== undefined) {\r\n form.append(\"only_center_face\", input.onlyCenterFace ? \"true\" : \"false\");\r\n }\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/face-restore\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Colorize a black-and-white or grayscale photo (DDColor v0.7.0+).\r\n *\r\n * Apache-2.0 model, single feed-forward (no diffusion), ~500ms warm on L4.\r\n * Works on any input — color photos are converted to grayscale internally\r\n * before color is predicted, which makes round-trip recoloring easy too.\r\n *\r\n * @example\r\n * const buf = await client.colorize({ file: \"./old-photo.jpg\" });\r\n * await writeFile(\"colorized.png\", buf);\r\n */\r\n async colorize(input: ColorizeInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/colorize\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Inpaint a region of an image using LaMa (Apache-2.0). Three modes,\r\n * auto-detected from what you pass:\r\n *\r\n * 1. **auto-subject** — pass only `file`. BiRefNet derives the subject\r\n * mask, inverts it, and LaMa fills the subject region with plausible\r\n * background. Drop in a photo, get the subject erased.\r\n * 2. **mask** — pass `file` + `mask` (any image, white = inpaint).\r\n * 3. **bbox** — pass `file` + `bbox: { x, y, w, h }`. Rectangular region.\r\n *\r\n * `dilation` (0..32, default 8) expands the mask before LaMa runs to\r\n * reduce ghost outlines from tight masks.\r\n *\r\n * @example Auto-erase subject\r\n * const png = await client.inpaint({ file: \"./photo.jpg\" });\r\n *\r\n * @example Erase a rectangular region\r\n * const png = await client.inpaint({ file: \"./photo.jpg\", bbox: { x: 100, y: 100, w: 300, h: 400 } });\r\n */\r\n async inpaint(input: InpaintInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.mask !== undefined) {\r\n const { blob: maskBlob, filename: maskFilename } = await toBlob({\r\n file: input.mask,\r\n filename: input.maskFilename ?? \"mask.png\",\r\n });\r\n form.append(\"mask\", maskBlob, maskFilename);\r\n }\r\n if (input.bbox) {\r\n form.append(\"x\", String(input.bbox.x));\r\n form.append(\"y\", String(input.bbox.y));\r\n form.append(\"w\", String(input.bbox.w));\r\n form.append(\"h\", String(input.bbox.h));\r\n }\r\n if (input.dilation !== undefined) form.append(\"dilation\", String(input.dilation));\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/inpaint\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Two-tone silhouette portrait — subject in one solid color, bg in another.\r\n * Apple Music / Spotify avatar aesthetic. Reuses BiRefNet mask path; no new\r\n * model load, fast.\r\n *\r\n * @example\r\n * const png = await client.silhouette({\r\n * file: \"./portrait.jpg\",\r\n * subjectColor: \"#1E2960\",\r\n * bgColor: \"#F0857C\",\r\n * });\r\n */\r\n async silhouette(input: SilhouetteInput): Promise<Buffer> {\r\n const format: OpaqueFormat = input.format ?? \"png\";\r\n const { blob, filename } = await toBlob({ file: input.file, filename: input.filename });\r\n const form = new FormData();\r\n form.append(\"file\", blob, filename);\r\n if (input.subjectColor) form.append(\"subject_color\", input.subjectColor);\r\n if (input.bgColor) form.append(\"bg_color\", input.bgColor);\r\n form.append(\"format\", format);\r\n const res = await this.request(\"POST\", \"/silhouette\", { body: form });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return Buffer.from(await res.arrayBuffer());\r\n }\r\n\r\n /**\r\n * Remove the background from up to 10 remote image URLs in a single call.\r\n *\r\n * @example\r\n * const batch = await client.removeBatchUrl({\r\n * urls: [\"https://a.jpg\", \"https://b.jpg\"],\r\n * });\r\n */\r\n async removeBatchUrl(input: BatchUrlInput): Promise<BatchResponse> {\r\n const format: OutputFormat = input.format ?? \"png\";\r\n if (input.urls.length === 0) throw new Error(\"At least one URL required\");\r\n if (input.urls.length > 10) throw new Error(\"Max 10 URLs per batch\");\r\n\r\n const res = await this.request(\"POST\", \"/remove-batch-url\", {\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ urls: input.urls, format }),\r\n });\r\n if (!res.ok) throw new KnockoutError(res.status, await res.text());\r\n return (await res.json()) as BatchResponse;\r\n }\r\n\r\n private async request(\r\n method: \"GET\" | \"POST\",\r\n path: string,\r\n init: { headers?: Record<string, string>; body?: BodyInit } = {}\r\n ): Promise<Response> {\r\n const url = `${this.baseUrl}${path}`;\r\n const headers: Record<string, string> = {\r\n \"User-Agent\": `useknockout-node/${SDK_VERSION}`,\r\n ...(init.headers ?? {}),\r\n };\r\n if (this.token) headers[\"Authorization\"] = `Bearer ${this.token}`;\r\n\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n try {\r\n return await this.fetchImpl(url, {\r\n method,\r\n headers,\r\n body: init.body,\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n\r\nasync function toBlob(\r\n input: { file: string | Buffer | Blob | ArrayBuffer | Uint8Array; filename?: string }\r\n): Promise<{ blob: Blob; filename: string }> {\r\n const { file } = input;\r\n const filename = input.filename ?? inferFilename(file);\r\n\r\n if (typeof file === \"string\") {\r\n const data = await readFile(file);\r\n return { blob: new Blob([new Uint8Array(data)]), filename };\r\n }\r\n if (file instanceof Blob) {\r\n return { blob: file, filename };\r\n }\r\n if (file instanceof ArrayBuffer) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (file instanceof Uint8Array) {\r\n return { blob: new Blob([new Uint8Array(file)]), filename };\r\n }\r\n if (Buffer.isBuffer(file)) {\r\n return {\r\n blob: new Blob([new Uint8Array(file)]),\r\n filename,\r\n };\r\n }\r\n throw new TypeError(\"Unsupported `file` input. Provide a path, Buffer, Blob, ArrayBuffer, or Uint8Array.\");\r\n}\r\n\r\nfunction inferFilename(file: string | Buffer | Blob | ArrayBuffer | Uint8Array): string {\r\n if (typeof file === \"string\") return basename(file) || \"image\";\r\n return \"image\";\r\n}\r\n\r\nexport default Knockout;\r\n"]}
|