@poof-bg/js 1.0.0 → 1.2.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 +108 -5
- package/dist/index.cjs +184 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +177 -26
- package/dist/index.d.ts +177 -26
- package/dist/index.js +184 -31
- package/dist/index.js.map +1 -1
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -73,6 +73,12 @@ var ERROR_MAP = {
|
|
|
73
73
|
validation_error: ValidationError,
|
|
74
74
|
missing_image: ValidationError,
|
|
75
75
|
image_too_large: ValidationError,
|
|
76
|
+
invalid_image: ValidationError,
|
|
77
|
+
video_too_large: ValidationError,
|
|
78
|
+
video_too_long: ValidationError,
|
|
79
|
+
invalid_video: ValidationError,
|
|
80
|
+
job_not_found: ValidationError,
|
|
81
|
+
job_failed: PoofError,
|
|
76
82
|
upstream_error: ServerError,
|
|
77
83
|
internal_server_error: ServerError
|
|
78
84
|
};
|
|
@@ -94,6 +100,15 @@ function detectMimeType(buffer) {
|
|
|
94
100
|
if (buffer[0] === 71 && buffer[1] === 73 && buffer[2] === 70) {
|
|
95
101
|
return "image/gif";
|
|
96
102
|
}
|
|
103
|
+
if (buffer[4] === 102 && buffer[5] === 116 && buffer[6] === 121 && buffer[7] === 112) {
|
|
104
|
+
if (buffer[8] === 113 && buffer[9] === 116) {
|
|
105
|
+
return "video/quicktime";
|
|
106
|
+
}
|
|
107
|
+
return "video/mp4";
|
|
108
|
+
}
|
|
109
|
+
if (buffer[0] === 26 && buffer[1] === 69 && buffer[2] === 223 && buffer[3] === 163) {
|
|
110
|
+
return "video/webm";
|
|
111
|
+
}
|
|
97
112
|
return "application/octet-stream";
|
|
98
113
|
}
|
|
99
114
|
function getExtension(mimeType) {
|
|
@@ -101,7 +116,10 @@ function getExtension(mimeType) {
|
|
|
101
116
|
"image/png": "png",
|
|
102
117
|
"image/jpeg": "jpg",
|
|
103
118
|
"image/webp": "webp",
|
|
104
|
-
"image/gif": "gif"
|
|
119
|
+
"image/gif": "gif",
|
|
120
|
+
"video/mp4": "mp4",
|
|
121
|
+
"video/quicktime": "mov",
|
|
122
|
+
"video/webm": "webm"
|
|
105
123
|
};
|
|
106
124
|
return map[mimeType] || "bin";
|
|
107
125
|
}
|
|
@@ -114,14 +132,10 @@ async function toBlob(input) {
|
|
|
114
132
|
const mimeType = detectMimeType(uint8);
|
|
115
133
|
return new Blob([input], { type: mimeType });
|
|
116
134
|
}
|
|
117
|
-
if (input instanceof Uint8Array) {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (isNode && Buffer.isBuffer(input)) {
|
|
122
|
-
const uint8 = new Uint8Array(input);
|
|
123
|
-
const mimeType = detectMimeType(uint8);
|
|
124
|
-
return new Blob([input], { type: mimeType });
|
|
135
|
+
if (input instanceof Uint8Array || isNode && Buffer.isBuffer(input)) {
|
|
136
|
+
const bytes = Uint8Array.from(input);
|
|
137
|
+
const mimeType = detectMimeType(bytes);
|
|
138
|
+
return new Blob([bytes], { type: mimeType });
|
|
125
139
|
}
|
|
126
140
|
if (typeof input === "string") {
|
|
127
141
|
if (!isNode) {
|
|
@@ -138,7 +152,12 @@ async function toBlob(input) {
|
|
|
138
152
|
jpg: "image/jpeg",
|
|
139
153
|
jpeg: "image/jpeg",
|
|
140
154
|
webp: "image/webp",
|
|
141
|
-
gif: "image/gif"
|
|
155
|
+
gif: "image/gif",
|
|
156
|
+
mp4: "video/mp4",
|
|
157
|
+
m4v: "video/mp4",
|
|
158
|
+
mov: "video/quicktime",
|
|
159
|
+
webm: "video/webm",
|
|
160
|
+
mkv: "video/x-matroska"
|
|
142
161
|
};
|
|
143
162
|
const finalMimeType = mimeFromExt[ext] || mimeType;
|
|
144
163
|
return new Blob([buffer], { type: finalMimeType });
|
|
@@ -151,14 +170,26 @@ function getFilename(input, mimeType) {
|
|
|
151
170
|
}
|
|
152
171
|
if (typeof input === "string" && isNode) {
|
|
153
172
|
const parts = input.split(/[/\\]/);
|
|
154
|
-
return parts[parts.length - 1] ||
|
|
173
|
+
return parts[parts.length - 1] || defaultFilename(mimeType);
|
|
155
174
|
}
|
|
156
|
-
return
|
|
175
|
+
return defaultFilename(mimeType);
|
|
176
|
+
}
|
|
177
|
+
function defaultFilename(mimeType) {
|
|
178
|
+
const stem = mimeType.startsWith("video/") ? "video" : "image";
|
|
179
|
+
return `${stem}.${getExtension(mimeType)}`;
|
|
180
|
+
}
|
|
181
|
+
function isUrlInput(input) {
|
|
182
|
+
return typeof input === "object" && input !== null && "url" in input && typeof input.url === "string";
|
|
183
|
+
}
|
|
184
|
+
function sleep(ms) {
|
|
185
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
157
186
|
}
|
|
158
187
|
|
|
159
188
|
// src/client.ts
|
|
160
189
|
var DEFAULT_BASE_URL = "https://api.poof.bg/v1";
|
|
161
190
|
var DEFAULT_TIMEOUT = 6e4;
|
|
191
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
192
|
+
var DEFAULT_JOB_TIMEOUT_MS = 6e5;
|
|
162
193
|
var Poof = class {
|
|
163
194
|
apiKey;
|
|
164
195
|
baseUrl;
|
|
@@ -172,43 +203,57 @@ var Poof = class {
|
|
|
172
203
|
this.timeout = options.timeout || DEFAULT_TIMEOUT;
|
|
173
204
|
}
|
|
174
205
|
/**
|
|
175
|
-
* Remove background from an image
|
|
206
|
+
* Remove the background from an image or a video.
|
|
207
|
+
*
|
|
208
|
+
* Images are processed synchronously and come back as bytes. Videos are
|
|
209
|
+
* processed as a job: the call returns a queued {@link Job}; poll it with
|
|
210
|
+
* {@link getJob} / {@link waitForJob}, or pass `wait: true` to get the
|
|
211
|
+
* completed job (with `resultUrl`) directly. Use {@link isJob} to tell the
|
|
212
|
+
* two apart.
|
|
176
213
|
*
|
|
177
|
-
* @param input -
|
|
214
|
+
* @param input - File, Blob, Buffer, ArrayBuffer, Uint8Array, file path (Node.js), or `{ url }`
|
|
178
215
|
* @param options - Processing options
|
|
179
|
-
* @returns Processed image data and metadata
|
|
216
|
+
* @returns Processed image data and metadata, or a video Job
|
|
180
217
|
*
|
|
181
218
|
* @example
|
|
182
219
|
* ```typescript
|
|
183
|
-
* //
|
|
220
|
+
* // Image
|
|
184
221
|
* const result = await poof.removeBackground(file);
|
|
185
222
|
* const imageBlob = new Blob([result.data], { type: result.metadata.contentType });
|
|
186
223
|
*
|
|
187
224
|
* // With options
|
|
188
|
-
* const result = await poof.removeBackground(file, {
|
|
189
|
-
* format: 'webp',
|
|
190
|
-
* crop: true,
|
|
191
|
-
* size: 'medium'
|
|
192
|
-
* });
|
|
225
|
+
* const result = await poof.removeBackground(file, { format: 'webp', crop: true, size: 'medium' });
|
|
193
226
|
*
|
|
194
|
-
* //
|
|
195
|
-
* const result = await poof.removeBackground(
|
|
227
|
+
* // Exact 500x500 output, subject cropped and centred, never stretched
|
|
228
|
+
* const result = await poof.removeBackground(file, { crop: true, width: 500, height: 500 });
|
|
196
229
|
*
|
|
197
|
-
* //
|
|
198
|
-
* const
|
|
199
|
-
*
|
|
230
|
+
* // Video: submit, then wait for the result
|
|
231
|
+
* const job = await poof.removeBackground('./clip.mp4', { format: 'webm' });
|
|
232
|
+
* const done = await poof.waitForJob(job);
|
|
233
|
+
* console.log(done.resultUrl);
|
|
234
|
+
*
|
|
235
|
+
* // Video from a public URL, waiting inline
|
|
236
|
+
* const done = await poof.removeBackground({ url: 'https://example.com/clip.mp4' }, { wait: true });
|
|
200
237
|
* ```
|
|
201
238
|
*/
|
|
202
239
|
async removeBackground(input, options = {}) {
|
|
203
|
-
const blob = await toBlob(input);
|
|
204
|
-
const filename = getFilename(input, blob.type);
|
|
205
240
|
const formData = new FormData();
|
|
206
|
-
|
|
241
|
+
if (isUrlInput(input)) {
|
|
242
|
+
formData.append("url", input.url);
|
|
243
|
+
} else {
|
|
244
|
+
const blob = await toBlob(input);
|
|
245
|
+
const filename = getFilename(input, blob.type);
|
|
246
|
+
formData.append("file", blob, filename);
|
|
247
|
+
}
|
|
207
248
|
if (options.format) formData.append("format", options.format);
|
|
208
249
|
if (options.channels) formData.append("channels", options.channels);
|
|
209
250
|
if (options.bgColor) formData.append("bg_color", options.bgColor);
|
|
210
251
|
if (options.size) formData.append("size", options.size);
|
|
211
252
|
if (options.crop !== void 0) formData.append("crop", String(options.crop));
|
|
253
|
+
if (options.padding) formData.append("padding", options.padding);
|
|
254
|
+
if (options.width !== void 0) formData.append("width", String(options.width));
|
|
255
|
+
if (options.height !== void 0) formData.append("height", String(options.height));
|
|
256
|
+
if (options.fit) formData.append("fit", options.fit);
|
|
212
257
|
const response = await this.fetch(`${this.baseUrl}/remove`, {
|
|
213
258
|
method: "POST",
|
|
214
259
|
body: formData
|
|
@@ -216,7 +261,16 @@ var Poof = class {
|
|
|
216
261
|
if (!response.ok) {
|
|
217
262
|
await this.handleError(response);
|
|
218
263
|
}
|
|
264
|
+
if (response.status === 202) {
|
|
265
|
+
const job = toJob(await response.json());
|
|
266
|
+
if (options.wait) {
|
|
267
|
+
return this.waitForJob(job, options.wait === true ? {} : options.wait);
|
|
268
|
+
}
|
|
269
|
+
return job;
|
|
270
|
+
}
|
|
219
271
|
const data = await response.arrayBuffer();
|
|
272
|
+
const matteConfidence = response.headers.get("X-Matte-Confidence");
|
|
273
|
+
const matteAmbiguousRatio = response.headers.get("X-Matte-Ambiguous-Ratio");
|
|
220
274
|
return {
|
|
221
275
|
data,
|
|
222
276
|
metadata: {
|
|
@@ -224,10 +278,80 @@ var Poof = class {
|
|
|
224
278
|
processingTimeMs: parseInt(response.headers.get("X-Processing-Time-Ms") || "0", 10),
|
|
225
279
|
width: parseInt(response.headers.get("X-Image-Width") || "0", 10),
|
|
226
280
|
height: parseInt(response.headers.get("X-Image-Height") || "0", 10),
|
|
227
|
-
contentType: response.headers.get("Content-Type") || "image/png"
|
|
281
|
+
contentType: response.headers.get("Content-Type") || "image/png",
|
|
282
|
+
matteConfidence: matteConfidence !== null ? parseFloat(matteConfidence) : void 0,
|
|
283
|
+
matteAmbiguousRatio: matteAmbiguousRatio !== null ? parseFloat(matteAmbiguousRatio) : void 0
|
|
228
284
|
}
|
|
229
285
|
};
|
|
230
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Get the current state of a video job. Polling is free and does not count
|
|
289
|
+
* against your rate limit.
|
|
290
|
+
*
|
|
291
|
+
* @param jobId - The job ID returned by removeBackground()
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* const job = await poof.getJob('job_8f3a1c2e9b7d4a10');
|
|
296
|
+
* console.log(job.status, job.progress);
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
async getJob(jobId) {
|
|
300
|
+
const response = await this.fetch(`${this.baseUrl}/jobs/${encodeURIComponent(jobId)}`, {
|
|
301
|
+
method: "GET",
|
|
302
|
+
headers: {
|
|
303
|
+
Accept: "application/json"
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
if (!response.ok) {
|
|
307
|
+
await this.handleError(response);
|
|
308
|
+
}
|
|
309
|
+
return toJob(await response.json());
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Poll a video job until it is completed.
|
|
313
|
+
*
|
|
314
|
+
* @param job - A Job or a job ID
|
|
315
|
+
* @param options - Poll interval, timeout and progress callback
|
|
316
|
+
* @returns The completed job; `resultUrl` is a direct download link valid for 24 hours
|
|
317
|
+
* @throws PoofError with code `job_failed` if the job fails (credits are refunded),
|
|
318
|
+
* or code `timeout` if `timeoutMs` elapses first
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* const done = await poof.waitForJob(job, {
|
|
323
|
+
* onProgress: (j) => console.log(`${Math.round(j.progress * 100)}%`),
|
|
324
|
+
* });
|
|
325
|
+
* const video = await fetch(done.resultUrl!);
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
async waitForJob(job, options = {}) {
|
|
329
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
330
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_JOB_TIMEOUT_MS;
|
|
331
|
+
const deadline = Date.now() + timeoutMs;
|
|
332
|
+
let current = typeof job === "string" ? await this.getJob(job) : job;
|
|
333
|
+
options.onProgress?.(current);
|
|
334
|
+
while (!isTerminal(current.status)) {
|
|
335
|
+
if (Date.now() >= deadline) {
|
|
336
|
+
throw new PoofError(
|
|
337
|
+
`Timed out after ${timeoutMs}ms waiting for job ${current.id}`,
|
|
338
|
+
"timeout",
|
|
339
|
+
{ status: 408 }
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
await sleep(Math.min(pollIntervalMs, Math.max(0, deadline - Date.now())));
|
|
343
|
+
current = await this.getJob(current.id);
|
|
344
|
+
options.onProgress?.(current);
|
|
345
|
+
}
|
|
346
|
+
if (current.status === "failed") {
|
|
347
|
+
throw new PoofError(
|
|
348
|
+
current.error?.message || `Job ${current.id} failed`,
|
|
349
|
+
"job_failed",
|
|
350
|
+
{ details: current.error?.code }
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return current;
|
|
354
|
+
}
|
|
231
355
|
/**
|
|
232
356
|
* Get account information
|
|
233
357
|
*
|
|
@@ -289,7 +413,36 @@ var Poof = class {
|
|
|
289
413
|
throw PoofError.fromResponse(errorData, response.status);
|
|
290
414
|
}
|
|
291
415
|
};
|
|
416
|
+
function isJob(value) {
|
|
417
|
+
return typeof value === "object" && value !== null && "status" in value && typeof value.id === "string";
|
|
418
|
+
}
|
|
419
|
+
function isTerminal(status) {
|
|
420
|
+
return status === "completed" || status === "failed";
|
|
421
|
+
}
|
|
422
|
+
function toJob(raw) {
|
|
423
|
+
return {
|
|
424
|
+
id: raw.id,
|
|
425
|
+
status: raw.status,
|
|
426
|
+
progress: raw.progress ?? 0,
|
|
427
|
+
createdAt: raw.created_at,
|
|
428
|
+
startedAt: raw.started_at ?? null,
|
|
429
|
+
completedAt: raw.completed_at ?? null,
|
|
430
|
+
durationSeconds: raw.duration_seconds,
|
|
431
|
+
fps: raw.fps,
|
|
432
|
+
width: raw.width,
|
|
433
|
+
height: raw.height,
|
|
434
|
+
frames: raw.frames,
|
|
435
|
+
credits: raw.credits,
|
|
436
|
+
format: raw.format,
|
|
437
|
+
channels: raw.channels,
|
|
438
|
+
statusUrl: raw.status_url,
|
|
439
|
+
resultUrl: raw.result_url ?? null,
|
|
440
|
+
expiresAt: raw.expires_at ?? null,
|
|
441
|
+
error: raw.error ?? null,
|
|
442
|
+
stats: raw.stats ?? null
|
|
443
|
+
};
|
|
444
|
+
}
|
|
292
445
|
|
|
293
|
-
export { AuthenticationError, PaymentRequiredError, PermissionError, Poof, PoofError, RateLimitError, ServerError, ValidationError };
|
|
446
|
+
export { AuthenticationError, PaymentRequiredError, PermissionError, Poof, PoofError, RateLimitError, ServerError, ValidationError, isJob };
|
|
294
447
|
//# sourceMappingURL=index.js.map
|
|
295
448
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ErrorCode, ApiErrorResponse } from './types.js';\n\n/**\n * Base error class for all Poof SDK errors\n */\nexport class PoofError extends Error {\n /** Machine-readable error code */\n readonly code: string;\n /** HTTP status code */\n readonly status?: number;\n /** Unique request ID for support */\n readonly requestId?: string;\n /** Additional details */\n readonly details?: string;\n\n constructor(\n message: string,\n code: string,\n options?: { status?: number; requestId?: string; details?: string }\n ) {\n super(message);\n this.name = 'PoofError';\n this.code = code;\n this.status = options?.status;\n this.requestId = options?.requestId;\n this.details = options?.details;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /** Create error from API response */\n static fromResponse(response: ApiErrorResponse, status: number): PoofError {\n const ErrorClass = ERROR_MAP[response.code] || PoofError;\n return new ErrorClass(response.message, response.code, {\n status,\n requestId: response.request_id,\n details: response.details,\n });\n }\n}\n\n/**\n * Thrown when the API key is invalid or missing\n */\nexport class AuthenticationError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown when the account doesn't have permission\n */\nexport class PermissionError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'PermissionError';\n }\n}\n\n/**\n * Thrown when the account has insufficient credits\n */\nexport class PaymentRequiredError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'PaymentRequiredError';\n }\n}\n\n/**\n * Thrown when rate limit is exceeded\n */\nexport class RateLimitError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'RateLimitError';\n }\n}\n\n/**\n * Thrown when request parameters are invalid\n */\nexport class ValidationError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Thrown when a server error occurs\n */\nexport class ServerError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'ServerError';\n }\n}\n\n/** Map error codes to error classes */\nconst ERROR_MAP: Record<ErrorCode, typeof PoofError> = {\n authentication_error: AuthenticationError,\n permission_denied: PermissionError,\n payment_required: PaymentRequiredError,\n rate_limit_exceeded: RateLimitError,\n validation_error: ValidationError,\n missing_image: ValidationError,\n image_too_large: ValidationError,\n upstream_error: ServerError,\n internal_server_error: ServerError,\n};\n","import type { ImageInput } from './types.js';\n\n/** Check if running in Node.js environment */\nexport const isNode = typeof process !== 'undefined' && process.versions?.node;\n\n/** Detect MIME type from buffer */\nfunction detectMimeType(buffer: Uint8Array): string {\n // Check magic bytes\n if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) {\n return 'image/png';\n }\n if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {\n return 'image/jpeg';\n }\n if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46) {\n // RIFF header, check for WEBP\n if (buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {\n return 'image/webp';\n }\n }\n if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {\n return 'image/gif';\n }\n // Default to octet-stream\n return 'application/octet-stream';\n}\n\n/** Get file extension from MIME type */\nfunction getExtension(mimeType: string): string {\n const map: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n };\n return map[mimeType] || 'bin';\n}\n\n/** Convert various input types to a Blob suitable for FormData */\nexport async function toBlob(input: ImageInput): Promise<Blob> {\n // Already a Blob or File\n if (input instanceof Blob) {\n return input;\n }\n\n // ArrayBuffer\n if (input instanceof ArrayBuffer) {\n const uint8 = new Uint8Array(input);\n const mimeType = detectMimeType(uint8);\n return new Blob([input], { type: mimeType });\n }\n\n // Uint8Array\n if (input instanceof Uint8Array) {\n const mimeType = detectMimeType(input);\n return new Blob([input], { type: mimeType });\n }\n\n // Node.js Buffer\n if (isNode && Buffer.isBuffer(input)) {\n const uint8 = new Uint8Array(input);\n const mimeType = detectMimeType(uint8);\n return new Blob([input], { type: mimeType });\n }\n\n // String - treat as file path (Node.js only)\n if (typeof input === 'string') {\n if (!isNode) {\n throw new Error('File paths are only supported in Node.js. Use a Blob or File in the browser.');\n }\n \n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n \n const buffer = await fs.readFile(input);\n const uint8 = new Uint8Array(buffer);\n const mimeType = detectMimeType(uint8);\n const ext = path.extname(input).toLowerCase().slice(1);\n \n // Use file extension for mime type if available\n const mimeFromExt: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n };\n \n const finalMimeType = mimeFromExt[ext] || mimeType;\n return new Blob([buffer], { type: finalMimeType });\n }\n\n throw new Error(`Unsupported input type: ${typeof input}`);\n}\n\n/** Get filename for FormData */\nexport function getFilename(input: ImageInput, mimeType: string): string {\n // File has a name\n if (typeof File !== 'undefined' && input instanceof File) {\n return input.name;\n }\n\n // String path\n if (typeof input === 'string' && isNode) {\n // Extract filename from path\n const parts = input.split(/[/\\\\]/);\n return parts[parts.length - 1] || `image.${getExtension(mimeType)}`;\n }\n\n // Generate name from mime type\n return `image.${getExtension(mimeType)}`;\n}\n","import type {\n PoofOptions,\n ImageInput,\n RemoveBackgroundOptions,\n RemoveBackgroundResult,\n AccountInfo,\n ApiErrorResponse,\n} from './types.js';\nimport { PoofError } from './errors.js';\nimport { toBlob, getFilename } from './utils.js';\n\nconst DEFAULT_BASE_URL = 'https://api.poof.bg/v1';\nconst DEFAULT_TIMEOUT = 60000;\n\n/**\n * Poof API client for background removal\n *\n * @example\n * ```typescript\n * import { Poof } from '@poof-bg/js';\n *\n * const poof = new Poof({ apiKey: 'your-api-key' });\n * const result = await poof.removeBackground(imageFile);\n * ```\n */\nexport class Poof {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(options: PoofOptions) {\n if (!options.apiKey) {\n throw new Error('API key is required');\n }\n\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl?.replace(/\\/$/, '') || DEFAULT_BASE_URL;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Remove background from an image\n *\n * @param input - Image input: File, Blob, Buffer, ArrayBuffer, Uint8Array, or file path (Node.js)\n * @param options - Processing options\n * @returns Processed image data and metadata\n *\n * @example\n * ```typescript\n * // Basic usage\n * const result = await poof.removeBackground(file);\n * const imageBlob = new Blob([result.data], { type: result.metadata.contentType });\n *\n * // With options\n * const result = await poof.removeBackground(file, {\n * format: 'webp',\n * crop: true,\n * size: 'medium'\n * });\n *\n * // Node.js: from file path\n * const result = await poof.removeBackground('/path/to/image.png');\n *\n * // Node.js: save to file\n * const fs = require('fs');\n * fs.writeFileSync('output.png', Buffer.from(result.data));\n * ```\n */\n async removeBackground(\n input: ImageInput,\n options: RemoveBackgroundOptions = {}\n ): Promise<RemoveBackgroundResult> {\n const blob = await toBlob(input);\n const filename = getFilename(input, blob.type);\n\n const formData = new FormData();\n formData.append('image_file', blob, filename);\n\n // Add optional parameters\n if (options.format) formData.append('format', options.format);\n if (options.channels) formData.append('channels', options.channels);\n if (options.bgColor) formData.append('bg_color', options.bgColor);\n if (options.size) formData.append('size', options.size);\n if (options.crop !== undefined) formData.append('crop', String(options.crop));\n\n const response = await this.fetch(`${this.baseUrl}/remove`, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n await this.handleError(response);\n }\n\n const data = await response.arrayBuffer();\n\n return {\n data,\n metadata: {\n requestId: response.headers.get('X-Request-ID') || '',\n processingTimeMs: parseInt(response.headers.get('X-Processing-Time-Ms') || '0', 10),\n width: parseInt(response.headers.get('X-Image-Width') || '0', 10),\n height: parseInt(response.headers.get('X-Image-Height') || '0', 10),\n contentType: response.headers.get('Content-Type') || 'image/png',\n },\n };\n }\n\n /**\n * Get account information\n *\n * @returns Account info including plan and credit usage\n *\n * @example\n * ```typescript\n * const account = await poof.me();\n * console.log(`Plan: ${account.plan}`);\n * console.log(`Credits: ${account.usedCredits}/${account.maxCredits}`);\n * ```\n */\n async me(): Promise<AccountInfo> {\n const response = await this.fetch(`${this.baseUrl}/me`, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n },\n });\n\n if (!response.ok) {\n await this.handleError(response);\n }\n\n return response.json();\n }\n\n /** Make a fetch request with auth and timeout */\n private async fetch(url: string, init: RequestInit): Promise<Response> {\n const headers = new Headers(init.headers);\n headers.set('x-api-key', this.apiKey);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n ...init,\n headers,\n signal: controller.signal,\n });\n return response;\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new PoofError('Request timeout', 'timeout', { status: 408 });\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /** Handle error response */\n private async handleError(response: Response): Promise<never> {\n let errorData: ApiErrorResponse;\n\n try {\n errorData = await response.json();\n } catch {\n throw new PoofError(\n `HTTP ${response.status}: ${response.statusText}`,\n 'unknown_error',\n { status: response.status }\n );\n }\n\n throw PoofError.fromResponse(errorData, response.status);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/client.ts"],"names":[],"mappings":";AAKO,IAAM,SAAA,GAAN,MAAM,UAAA,SAAkB,KAAA,CAAM;AAAA;AAAA,EAE1B,IAAA;AAAA;AAAA,EAEA,MAAA;AAAA;AAAA,EAEA,SAAA;AAAA;AAAA,EAEA,OAAA;AAAA,EAET,WAAA,CACE,OAAA,EACA,IAAA,EACA,OAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,WAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,SAAS,OAAA,EAAS,MAAA;AACvB,IAAA,IAAA,CAAK,YAAY,OAAA,EAAS,SAAA;AAC1B,IAAA,IAAA,CAAK,UAAU,OAAA,EAAS,OAAA;AAGxB,IAAA,IAAI,mBAAA,IAAuB,KAAA,IAAS,OAAO,KAAA,CAAM,sBAAsB,UAAA,EAAY;AACjF,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,IAChD;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,YAAA,CAAa,QAAA,EAA4B,MAAA,EAA2B;AACzE,IAAA,MAAM,UAAA,GAAa,SAAA,CAAU,QAAA,CAAS,IAAI,CAAA,IAAK,UAAA;AAC/C,IAAA,OAAO,IAAI,UAAA,CAAW,QAAA,CAAS,OAAA,EAAS,SAAS,IAAA,EAAM;AAAA,MACrD,MAAA;AAAA,MACA,WAAW,QAAA,CAAS,UAAA;AAAA,MACpB,SAAS,QAAA,CAAS;AAAA,KACnB,CAAA;AAAA,EACH;AACF;AAKO,IAAM,mBAAA,GAAN,cAAkC,SAAA,CAAU;AAAA,EACjD,WAAA,CAAY,OAAA,EAAiB,IAAA,EAAc,OAAA,EAAqE;AAC9G,IAAA,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAKO,IAAM,eAAA,GAAN,cAA8B,SAAA,CAAU;AAAA,EAC7C,WAAA,CAAY,OAAA,EAAiB,IAAA,EAAc,OAAA,EAAqE;AAC9G,IAAA,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAKO,IAAM,oBAAA,GAAN,cAAmC,SAAA,CAAU;AAAA,EAClD,WAAA,CAAY,OAAA,EAAiB,IAAA,EAAc,OAAA,EAAqE;AAC9G,IAAA,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AAAA,EACd;AACF;AAKO,IAAM,cAAA,GAAN,cAA6B,SAAA,CAAU;AAAA,EAC5C,WAAA,CAAY,OAAA,EAAiB,IAAA,EAAc,OAAA,EAAqE;AAC9G,IAAA,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AAAA,EACd;AACF;AAKO,IAAM,eAAA,GAAN,cAA8B,SAAA,CAAU;AAAA,EAC7C,WAAA,CAAY,OAAA,EAAiB,IAAA,EAAc,OAAA,EAAqE;AAC9G,IAAA,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAKO,IAAM,WAAA,GAAN,cAA0B,SAAA,CAAU;AAAA,EACzC,WAAA,CAAY,OAAA,EAAiB,IAAA,EAAc,OAAA,EAAqE;AAC9G,IAAA,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AAAA,EACd;AACF;AAGA,IAAM,SAAA,GAAiD;AAAA,EACrD,oBAAA,EAAsB,mBAAA;AAAA,EACtB,iBAAA,EAAmB,eAAA;AAAA,EACnB,gBAAA,EAAkB,oBAAA;AAAA,EAClB,mBAAA,EAAqB,cAAA;AAAA,EACrB,gBAAA,EAAkB,eAAA;AAAA,EAClB,aAAA,EAAe,eAAA;AAAA,EACf,eAAA,EAAiB,eAAA;AAAA,EACjB,aAAA,EAAe,eAAA;AAAA,EACf,eAAA,EAAiB,eAAA;AAAA,EACjB,cAAA,EAAgB,eAAA;AAAA,EAChB,aAAA,EAAe,eAAA;AAAA,EACf,aAAA,EAAe,eAAA;AAAA,EACf,UAAA,EAAY,SAAA;AAAA,EACZ,cAAA,EAAgB,WAAA;AAAA,EAChB,qBAAA,EAAuB;AACzB,CAAA;;;ACtHO,IAAM,MAAA,GAAS,OAAO,OAAA,KAAY,WAAA,IAAe,QAAQ,QAAA,EAAU,IAAA;AAG1E,SAAS,eAAe,MAAA,EAA4B;AAElD,EAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,OAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,MAAM,EAAA,EAAM;AACxF,IAAA,OAAO,WAAA;AAAA,EACT;AACA,EAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,EAAM;AAClE,IAAA,OAAO,YAAA;AAAA,EACT;AACA,EAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,OAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,MAAM,EAAA,EAAM;AAExF,IAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,OAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,EAAE,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,EAAE,MAAM,EAAA,EAAM;AAC1F,MAAA,OAAO,YAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,EAAM;AAClE,IAAA,OAAO,WAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,OAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,MAAA,CAAO,CAAC,MAAM,GAAA,EAAM;AAExF,IAAA,IAAI,OAAO,CAAC,CAAA,KAAM,OAAQ,MAAA,CAAO,CAAC,MAAM,GAAA,EAAM;AAC5C,MAAA,OAAO,iBAAA;AAAA,IACT;AACA,IAAA,OAAO,WAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,OAAO,CAAC,CAAA,KAAM,EAAA,IAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,MAAA,CAAO,CAAC,MAAM,GAAA,EAAM;AACxF,IAAA,OAAO,YAAA;AAAA,EACT;AAEA,EAAA,OAAO,0BAAA;AACT;AAGA,SAAS,aAAa,QAAA,EAA0B;AAC9C,EAAA,MAAM,GAAA,GAA8B;AAAA,IAClC,WAAA,EAAa,KAAA;AAAA,IACb,YAAA,EAAc,KAAA;AAAA,IACd,YAAA,EAAc,MAAA;AAAA,IACd,WAAA,EAAa,KAAA;AAAA,IACb,WAAA,EAAa,KAAA;AAAA,IACb,iBAAA,EAAmB,KAAA;AAAA,IACnB,YAAA,EAAc;AAAA,GAChB;AACA,EAAA,OAAO,GAAA,CAAI,QAAQ,CAAA,IAAK,KAAA;AAC1B;AAGA,eAAsB,OAAO,KAAA,EAAkC;AAE7D,EAAA,IAAI,iBAAiB,IAAA,EAAM;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,IAAI,iBAAiB,WAAA,EAAa;AAChC,IAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,IAAA,MAAM,QAAA,GAAW,eAAe,KAAK,CAAA;AACrC,IAAA,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,IAAA,EAAM,UAAU,CAAA;AAAA,EAC7C;AAIA,EAAA,IAAI,iBAAiB,UAAA,IAAe,MAAA,IAAU,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAI;AACrE,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,CAAK,KAAK,CAAA;AACnC,IAAA,MAAM,QAAA,GAAW,eAAe,KAAK,CAAA;AACrC,IAAA,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,IAAA,EAAM,UAAU,CAAA;AAAA,EAC7C;AAGA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,MAAM,8EAA8E,CAAA;AAAA,IAChG;AAEA,IAAA,MAAM,EAAA,GAAK,MAAM,OAAO,aAAkB,CAAA;AAC1C,IAAA,MAAM,IAAA,GAAO,MAAM,OAAO,MAAW,CAAA;AAErC,IAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,QAAA,CAAS,KAAK,CAAA;AACtC,IAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,MAAM,CAAA;AACnC,IAAA,MAAM,QAAA,GAAW,eAAe,KAAK,CAAA;AACrC,IAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,KAAK,EAAE,WAAA,EAAY,CAAE,MAAM,CAAC,CAAA;AAGrD,IAAA,MAAM,WAAA,GAAsC;AAAA,MAC1C,GAAA,EAAK,WAAA;AAAA,MACL,GAAA,EAAK,YAAA;AAAA,MACL,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM,YAAA;AAAA,MACN,GAAA,EAAK,WAAA;AAAA,MACL,GAAA,EAAK,WAAA;AAAA,MACL,GAAA,EAAK,WAAA;AAAA,MACL,GAAA,EAAK,iBAAA;AAAA,MACL,IAAA,EAAM,YAAA;AAAA,MACN,GAAA,EAAK;AAAA,KACP;AAEA,IAAA,MAAM,aAAA,GAAgB,WAAA,CAAY,GAAG,CAAA,IAAK,QAAA;AAC1C,IAAA,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,IAAA,EAAM,eAAe,CAAA;AAAA,EACnD;AAEA,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,OAAO,KAAK,CAAA,CAAE,CAAA;AAC3D;AAGO,SAAS,WAAA,CAAY,OAAmB,QAAA,EAA0B;AAEvE,EAAA,IAAI,OAAO,IAAA,KAAS,WAAA,IAAe,KAAA,YAAiB,IAAA,EAAM;AACxD,IAAA,OAAO,KAAA,CAAM,IAAA;AAAA,EACf;AAGA,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,MAAA,EAAQ;AAEvC,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,OAAO,CAAA;AACjC,IAAA,OAAO,MAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,IAAK,gBAAgB,QAAQ,CAAA;AAAA,EAC5D;AAGA,EAAA,OAAO,gBAAgB,QAAQ,CAAA;AACjC;AAEA,SAAS,gBAAgB,QAAA,EAA0B;AACjD,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,UAAA,CAAW,QAAQ,IAAI,OAAA,GAAU,OAAA;AACvD,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,YAAA,CAAa,QAAQ,CAAC,CAAA,CAAA;AAC1C;AAGO,SAAS,WAAW,KAAA,EAAsC;AAC/D,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,KAAA,IAAS,KAAA,IAAS,OAAQ,KAAA,CAAmB,GAAA,KAAQ,QAAA;AAC7G;AAGO,SAAS,MAAM,EAAA,EAA2B;AAC/C,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;;;AC/HA,IAAM,gBAAA,GAAmB,wBAAA;AACzB,IAAM,eAAA,GAAkB,GAAA;AACxB,IAAM,wBAAA,GAA2B,GAAA;AACjC,IAAM,sBAAA,GAAyB,GAAA;AAaxB,IAAM,OAAN,MAAW;AAAA,EACC,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EAEjB,YAAY,OAAA,EAAsB;AAChC,IAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,MAAA,MAAM,IAAI,MAAM,qBAAqB,CAAA;AAAA,IACvC;AAEA,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA,EAAS,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,IAAK,gBAAA;AACtD,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,OAAA,IAAW,eAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,MAAM,gBAAA,CACJ,KAAA,EACA,OAAA,GAAmC,EAAC,EACG;AACvC,IAAA,MAAM,QAAA,GAAW,IAAI,QAAA,EAAS;AAE9B,IAAA,IAAI,UAAA,CAAW,KAAK,CAAA,EAAG;AACrB,MAAA,QAAA,CAAS,MAAA,CAAO,KAAA,EAAO,KAAA,CAAM,GAAG,CAAA;AAAA,IAClC,CAAA,MAAO;AACL,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,KAAK,CAAA;AAC/B,MAAA,MAAM,QAAA,GAAW,WAAA,CAAY,KAAA,EAAO,IAAA,CAAK,IAAI,CAAA;AAC7C,MAAA,QAAA,CAAS,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAAA,IACxC;AAGA,IAAA,IAAI,QAAQ,MAAA,EAAQ,QAAA,CAAS,MAAA,CAAO,QAAA,EAAU,QAAQ,MAAM,CAAA;AAC5D,IAAA,IAAI,QAAQ,QAAA,EAAU,QAAA,CAAS,MAAA,CAAO,UAAA,EAAY,QAAQ,QAAQ,CAAA;AAClE,IAAA,IAAI,QAAQ,OAAA,EAAS,QAAA,CAAS,MAAA,CAAO,UAAA,EAAY,QAAQ,OAAO,CAAA;AAChE,IAAA,IAAI,QAAQ,IAAA,EAAM,QAAA,CAAS,MAAA,CAAO,MAAA,EAAQ,QAAQ,IAAI,CAAA;AACtD,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW,QAAA,CAAS,OAAO,MAAA,EAAQ,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAC5E,IAAA,IAAI,QAAQ,OAAA,EAAS,QAAA,CAAS,MAAA,CAAO,SAAA,EAAW,QAAQ,OAAO,CAAA;AAC/D,IAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,EAAW,QAAA,CAAS,OAAO,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC/E,IAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAW,QAAA,CAAS,OAAO,QAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAC,CAAA;AAClF,IAAA,IAAI,QAAQ,GAAA,EAAK,QAAA,CAAS,MAAA,CAAO,KAAA,EAAO,QAAQ,GAAG,CAAA;AAEnD,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,MAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,OAAA,CAAA,EAAW;AAAA,MAC1D,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM;AAAA,KACP,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAA,CAAK,YAAY,QAAQ,CAAA;AAAA,IACjC;AAGA,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,MAAM,QAAA,CAAS,MAAM,CAAA;AACvC,MAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,QAAA,OAAO,IAAA,CAAK,WAAW,GAAA,EAAK,OAAA,CAAQ,SAAS,IAAA,GAAO,EAAC,GAAI,OAAA,CAAQ,IAAI,CAAA;AAAA,MACvE;AACA,MAAA,OAAO,GAAA;AAAA,IACT;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,WAAA,EAAY;AAExC,IAAA,MAAM,eAAA,GAAkB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,oBAAoB,CAAA;AACjE,IAAA,MAAM,mBAAA,GAAsB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,yBAAyB,CAAA;AAE1E,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,QAAA,EAAU;AAAA,QACR,SAAA,EAAW,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AAAA,QACnD,gBAAA,EAAkB,SAAS,QAAA,CAAS,OAAA,CAAQ,IAAI,sBAAsB,CAAA,IAAK,KAAK,EAAE,CAAA;AAAA,QAClF,KAAA,EAAO,SAAS,QAAA,CAAS,OAAA,CAAQ,IAAI,eAAe,CAAA,IAAK,KAAK,EAAE,CAAA;AAAA,QAChE,MAAA,EAAQ,SAAS,QAAA,CAAS,OAAA,CAAQ,IAAI,gBAAgB,CAAA,IAAK,KAAK,EAAE,CAAA;AAAA,QAClE,WAAA,EAAa,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,WAAA;AAAA,QACrD,eAAA,EAAiB,eAAA,KAAoB,IAAA,GAAO,UAAA,CAAW,eAAe,CAAA,GAAI,MAAA;AAAA,QAC1E,mBAAA,EAAqB,mBAAA,KAAwB,IAAA,GAAO,UAAA,CAAW,mBAAmB,CAAA,GAAI;AAAA;AACxF,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,OAAO,KAAA,EAA6B;AACxC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,MAAA,EAAS,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA,EAAI;AAAA,MACrF,MAAA,EAAQ,KAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,MAAA,EAAQ;AAAA;AACV,KACD,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAA,CAAK,YAAY,QAAQ,CAAA;AAAA,IACjC;AAEA,IAAA,OAAO,KAAA,CAAM,MAAM,QAAA,CAAS,IAAA,EAAM,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,UAAA,CAAW,GAAA,EAAmB,OAAA,GAA6B,EAAC,EAAiB;AACjF,IAAA,MAAM,cAAA,GAAiB,QAAQ,cAAA,IAAkB,wBAAA;AACjD,IAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,sBAAA;AACvC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAE9B,IAAA,IAAI,OAAA,GAAe,OAAO,GAAA,KAAQ,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,GAAG,CAAA,GAAI,GAAA;AACtE,IAAA,OAAA,CAAQ,aAAa,OAAO,CAAA;AAE5B,IAAA,OAAO,CAAC,UAAA,CAAW,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClC,MAAA,IAAI,IAAA,CAAK,GAAA,EAAI,IAAK,QAAA,EAAU;AAC1B,QAAA,MAAM,IAAI,SAAA;AAAA,UACR,CAAA,gBAAA,EAAmB,SAAS,CAAA,mBAAA,EAAsB,OAAA,CAAQ,EAAE,CAAA,CAAA;AAAA,UAC5D,SAAA;AAAA,UACA,EAAE,QAAQ,GAAA;AAAI,SAChB;AAAA,MACF;AACA,MAAA,MAAM,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAA,GAAW,IAAA,CAAK,GAAA,EAAK,CAAC,CAAC,CAAA;AACxE,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,EAAE,CAAA;AACtC,MAAA,OAAA,CAAQ,aAAa,OAAO,CAAA;AAAA,IAC9B;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,QAAA,EAAU;AAC/B,MAAA,MAAM,IAAI,SAAA;AAAA,QACR,OAAA,CAAQ,KAAA,EAAO,OAAA,IAAW,CAAA,IAAA,EAAO,QAAQ,EAAE,CAAA,OAAA,CAAA;AAAA,QAC3C,YAAA;AAAA,QACA,EAAE,OAAA,EAAS,OAAA,CAAQ,KAAA,EAAO,IAAA;AAAK,OACjC;AAAA,IACF;AAEA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,EAAA,GAA2B;AAC/B,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,MAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,GAAA,CAAA,EAAO;AAAA,MACtD,MAAA,EAAQ,KAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,MAAA,EAAQ;AAAA;AACV,KACD,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAA,CAAK,YAAY,QAAQ,CAAA;AAAA,IACjC;AAEA,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB;AAAA;AAAA,EAGA,MAAc,KAAA,CAAM,GAAA,EAAa,IAAA,EAAsC;AACrE,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA;AACxC,IAAA,OAAA,CAAQ,GAAA,CAAI,WAAA,EAAa,IAAA,CAAK,MAAM,CAAA;AAEpC,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,GAAG,IAAA;AAAA,QACH,OAAA;AAAA,QACA,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AACD,MAAA,OAAO,QAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,QAAA,MAAM,IAAI,SAAA,CAAU,iBAAA,EAAmB,WAAW,EAAE,MAAA,EAAQ,KAAK,CAAA;AAAA,MACnE;AACA,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,SAAS,CAAA;AAAA,IACxB;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,YAAY,QAAA,EAAoC;AAC5D,IAAA,IAAI,SAAA;AAEJ,IAAA,IAAI;AACF,MAAA,SAAA,GAAY,MAAM,SAAS,IAAA,EAAK;AAAA,IAClC,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,SAAA;AAAA,QACR,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA,CAAA;AAAA,QAC/C,eAAA;AAAA,QACA,EAAE,MAAA,EAAQ,QAAA,CAAS,MAAA;AAAO,OAC5B;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,CAAU,YAAA,CAAa,SAAA,EAAW,QAAA,CAAS,MAAM,CAAA;AAAA,EACzD;AACF;AAeO,SAAS,MAAM,KAAA,EAAmD;AACvE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,QAAA,IAAY,KAAA,IAAS,OAAQ,KAAA,CAAc,EAAA,KAAO,QAAA;AAC1G;AAEA,SAAS,WAAW,MAAA,EAA4B;AAC9C,EAAA,OAAO,MAAA,KAAW,eAAe,MAAA,KAAW,QAAA;AAC9C;AAyBA,SAAS,MAAM,GAAA,EAAkB;AAC/B,EAAA,OAAO;AAAA,IACL,IAAI,GAAA,CAAI,EAAA;AAAA,IACR,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,QAAA,EAAU,IAAI,QAAA,IAAY,CAAA;AAAA,IAC1B,WAAW,GAAA,CAAI,UAAA;AAAA,IACf,SAAA,EAAW,IAAI,UAAA,IAAc,IAAA;AAAA,IAC7B,WAAA,EAAa,IAAI,YAAA,IAAgB,IAAA;AAAA,IACjC,iBAAiB,GAAA,CAAI,gBAAA;AAAA,IACrB,KAAK,GAAA,CAAI,GAAA;AAAA,IACT,OAAO,GAAA,CAAI,KAAA;AAAA,IACX,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,SAAS,GAAA,CAAI,OAAA;AAAA,IACb,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,UAAU,GAAA,CAAI,QAAA;AAAA,IACd,WAAW,GAAA,CAAI,UAAA;AAAA,IACf,SAAA,EAAW,IAAI,UAAA,IAAc,IAAA;AAAA,IAC7B,SAAA,EAAW,IAAI,UAAA,IAAc,IAAA;AAAA,IAC7B,KAAA,EAAO,IAAI,KAAA,IAAS,IAAA;AAAA,IACpB,KAAA,EAAO,IAAI,KAAA,IAAS;AAAA,GACtB;AACF","file":"index.js","sourcesContent":["import type { ErrorCode, ApiErrorResponse } from './types.js';\n\n/**\n * Base error class for all Poof SDK errors\n */\nexport class PoofError extends Error {\n /** Machine-readable error code */\n readonly code: string;\n /** HTTP status code */\n readonly status?: number;\n /** Unique request ID for support */\n readonly requestId?: string;\n /** Additional details */\n readonly details?: string;\n\n constructor(\n message: string,\n code: string,\n options?: { status?: number; requestId?: string; details?: string }\n ) {\n super(message);\n this.name = 'PoofError';\n this.code = code;\n this.status = options?.status;\n this.requestId = options?.requestId;\n this.details = options?.details;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /** Create error from API response */\n static fromResponse(response: ApiErrorResponse, status: number): PoofError {\n const ErrorClass = ERROR_MAP[response.code] || PoofError;\n return new ErrorClass(response.message, response.code, {\n status,\n requestId: response.request_id,\n details: response.details,\n });\n }\n}\n\n/**\n * Thrown when the API key is invalid or missing\n */\nexport class AuthenticationError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown when the account doesn't have permission\n */\nexport class PermissionError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'PermissionError';\n }\n}\n\n/**\n * Thrown when the account has insufficient credits\n */\nexport class PaymentRequiredError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'PaymentRequiredError';\n }\n}\n\n/**\n * Thrown when rate limit is exceeded\n */\nexport class RateLimitError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'RateLimitError';\n }\n}\n\n/**\n * Thrown when request parameters are invalid\n */\nexport class ValidationError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Thrown when a server error occurs\n */\nexport class ServerError extends PoofError {\n constructor(message: string, code: string, options?: { status?: number; requestId?: string; details?: string }) {\n super(message, code, options);\n this.name = 'ServerError';\n }\n}\n\n/** Map error codes to error classes */\nconst ERROR_MAP: Record<ErrorCode, typeof PoofError> = {\n authentication_error: AuthenticationError,\n permission_denied: PermissionError,\n payment_required: PaymentRequiredError,\n rate_limit_exceeded: RateLimitError,\n validation_error: ValidationError,\n missing_image: ValidationError,\n image_too_large: ValidationError,\n invalid_image: ValidationError,\n video_too_large: ValidationError,\n video_too_long: ValidationError,\n invalid_video: ValidationError,\n job_not_found: ValidationError,\n job_failed: PoofError,\n upstream_error: ServerError,\n internal_server_error: ServerError,\n};\n","import type { ImageInput, MediaInput, UrlInput } from './types.js';\n\n/** Check if running in Node.js environment */\nexport const isNode = typeof process !== 'undefined' && process.versions?.node;\n\n/** Detect MIME type from buffer */\nfunction detectMimeType(buffer: Uint8Array): string {\n // Check magic bytes\n if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) {\n return 'image/png';\n }\n if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {\n return 'image/jpeg';\n }\n if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46) {\n // RIFF header, check for WEBP\n if (buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {\n return 'image/webp';\n }\n }\n if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {\n return 'image/gif';\n }\n // ISO base media file (MP4, MOV): 'ftyp' at offset 4\n if (buffer[4] === 0x66 && buffer[5] === 0x74 && buffer[6] === 0x79 && buffer[7] === 0x70) {\n // 'qt ' brand is QuickTime\n if (buffer[8] === 0x71 && buffer[9] === 0x74) {\n return 'video/quicktime';\n }\n return 'video/mp4';\n }\n // EBML header (WebM, Matroska)\n if (buffer[0] === 0x1a && buffer[1] === 0x45 && buffer[2] === 0xdf && buffer[3] === 0xa3) {\n return 'video/webm';\n }\n // Default to octet-stream\n return 'application/octet-stream';\n}\n\n/** Get file extension from MIME type */\nfunction getExtension(mimeType: string): string {\n const map: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'video/mp4': 'mp4',\n 'video/quicktime': 'mov',\n 'video/webm': 'webm',\n };\n return map[mimeType] || 'bin';\n}\n\n/** Convert various input types to a Blob suitable for FormData */\nexport async function toBlob(input: ImageInput): Promise<Blob> {\n // Already a Blob or File\n if (input instanceof Blob) {\n return input;\n }\n\n // ArrayBuffer\n if (input instanceof ArrayBuffer) {\n const uint8 = new Uint8Array(input);\n const mimeType = detectMimeType(uint8);\n return new Blob([input], { type: mimeType });\n }\n\n // Uint8Array or Node.js Buffer. Copy into a fresh ArrayBuffer-backed view:\n // a view over a SharedArrayBuffer (or a Buffer pool slice) is not a valid BlobPart.\n if (input instanceof Uint8Array || (isNode && Buffer.isBuffer(input))) {\n const bytes = Uint8Array.from(input);\n const mimeType = detectMimeType(bytes);\n return new Blob([bytes], { type: mimeType });\n }\n\n // String - treat as file path (Node.js only)\n if (typeof input === 'string') {\n if (!isNode) {\n throw new Error('File paths are only supported in Node.js. Use a Blob or File in the browser.');\n }\n \n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n \n const buffer = await fs.readFile(input);\n const uint8 = new Uint8Array(buffer);\n const mimeType = detectMimeType(uint8);\n const ext = path.extname(input).toLowerCase().slice(1);\n \n // Use file extension for mime type if available\n const mimeFromExt: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n mp4: 'video/mp4',\n m4v: 'video/mp4',\n mov: 'video/quicktime',\n webm: 'video/webm',\n mkv: 'video/x-matroska',\n };\n \n const finalMimeType = mimeFromExt[ext] || mimeType;\n return new Blob([buffer], { type: finalMimeType });\n }\n\n throw new Error(`Unsupported input type: ${typeof input}`);\n}\n\n/** Get filename for FormData */\nexport function getFilename(input: ImageInput, mimeType: string): string {\n // File has a name\n if (typeof File !== 'undefined' && input instanceof File) {\n return input.name;\n }\n\n // String path\n if (typeof input === 'string' && isNode) {\n // Extract filename from path\n const parts = input.split(/[/\\\\]/);\n return parts[parts.length - 1] || defaultFilename(mimeType);\n }\n\n // Generate name from mime type\n return defaultFilename(mimeType);\n}\n\nfunction defaultFilename(mimeType: string): string {\n const stem = mimeType.startsWith('video/') ? 'video' : 'image';\n return `${stem}.${getExtension(mimeType)}`;\n}\n\n/** Is the input a `{ url }` object rather than file contents? */\nexport function isUrlInput(input: MediaInput): input is UrlInput {\n return typeof input === 'object' && input !== null && 'url' in input && typeof (input as UrlInput).url === 'string';\n}\n\n/** Resolve after `ms` milliseconds */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type {\n PoofOptions,\n MediaInput,\n RemoveBackgroundOptions,\n RemoveBackgroundResult,\n Job,\n JobStatus,\n WaitForJobOptions,\n AccountInfo,\n ApiErrorResponse,\n} from './types.js';\nimport { PoofError } from './errors.js';\nimport { toBlob, getFilename, isUrlInput, sleep } from './utils.js';\n\nconst DEFAULT_BASE_URL = 'https://api.poof.bg/v1';\nconst DEFAULT_TIMEOUT = 60000;\nconst DEFAULT_POLL_INTERVAL_MS = 3000;\nconst DEFAULT_JOB_TIMEOUT_MS = 600000;\n\n/**\n * Poof API client for background removal\n *\n * @example\n * ```typescript\n * import { Poof } from '@poof-bg/js';\n *\n * const poof = new Poof({ apiKey: 'your-api-key' });\n * const result = await poof.removeBackground(imageFile);\n * ```\n */\nexport class Poof {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(options: PoofOptions) {\n if (!options.apiKey) {\n throw new Error('API key is required');\n }\n\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl?.replace(/\\/$/, '') || DEFAULT_BASE_URL;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Remove the background from an image or a video.\n *\n * Images are processed synchronously and come back as bytes. Videos are\n * processed as a job: the call returns a queued {@link Job}; poll it with\n * {@link getJob} / {@link waitForJob}, or pass `wait: true` to get the\n * completed job (with `resultUrl`) directly. Use {@link isJob} to tell the\n * two apart.\n *\n * @param input - File, Blob, Buffer, ArrayBuffer, Uint8Array, file path (Node.js), or `{ url }`\n * @param options - Processing options\n * @returns Processed image data and metadata, or a video Job\n *\n * @example\n * ```typescript\n * // Image\n * const result = await poof.removeBackground(file);\n * const imageBlob = new Blob([result.data], { type: result.metadata.contentType });\n *\n * // With options\n * const result = await poof.removeBackground(file, { format: 'webp', crop: true, size: 'medium' });\n *\n * // Exact 500x500 output, subject cropped and centred, never stretched\n * const result = await poof.removeBackground(file, { crop: true, width: 500, height: 500 });\n *\n * // Video: submit, then wait for the result\n * const job = await poof.removeBackground('./clip.mp4', { format: 'webm' });\n * const done = await poof.waitForJob(job);\n * console.log(done.resultUrl);\n *\n * // Video from a public URL, waiting inline\n * const done = await poof.removeBackground({ url: 'https://example.com/clip.mp4' }, { wait: true });\n * ```\n */\n async removeBackground(\n input: MediaInput,\n options: RemoveBackgroundOptions = {}\n ): Promise<RemoveBackgroundResult | Job> {\n const formData = new FormData();\n\n if (isUrlInput(input)) {\n formData.append('url', input.url);\n } else {\n const blob = await toBlob(input);\n const filename = getFilename(input, blob.type);\n formData.append('file', blob, filename);\n }\n\n // Add optional parameters\n if (options.format) formData.append('format', options.format);\n if (options.channels) formData.append('channels', options.channels);\n if (options.bgColor) formData.append('bg_color', options.bgColor);\n if (options.size) formData.append('size', options.size);\n if (options.crop !== undefined) formData.append('crop', String(options.crop));\n if (options.padding) formData.append('padding', options.padding);\n if (options.width !== undefined) formData.append('width', String(options.width));\n if (options.height !== undefined) formData.append('height', String(options.height));\n if (options.fit) formData.append('fit', options.fit);\n\n const response = await this.fetch(`${this.baseUrl}/remove`, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n await this.handleError(response);\n }\n\n // Videos are accepted as a job\n if (response.status === 202) {\n const job = toJob(await response.json());\n if (options.wait) {\n return this.waitForJob(job, options.wait === true ? {} : options.wait);\n }\n return job;\n }\n\n const data = await response.arrayBuffer();\n\n const matteConfidence = response.headers.get('X-Matte-Confidence');\n const matteAmbiguousRatio = response.headers.get('X-Matte-Ambiguous-Ratio');\n\n return {\n data,\n metadata: {\n requestId: response.headers.get('X-Request-ID') || '',\n processingTimeMs: parseInt(response.headers.get('X-Processing-Time-Ms') || '0', 10),\n width: parseInt(response.headers.get('X-Image-Width') || '0', 10),\n height: parseInt(response.headers.get('X-Image-Height') || '0', 10),\n contentType: response.headers.get('Content-Type') || 'image/png',\n matteConfidence: matteConfidence !== null ? parseFloat(matteConfidence) : undefined,\n matteAmbiguousRatio: matteAmbiguousRatio !== null ? parseFloat(matteAmbiguousRatio) : undefined,\n },\n };\n }\n\n /**\n * Get the current state of a video job. Polling is free and does not count\n * against your rate limit.\n *\n * @param jobId - The job ID returned by removeBackground()\n *\n * @example\n * ```typescript\n * const job = await poof.getJob('job_8f3a1c2e9b7d4a10');\n * console.log(job.status, job.progress);\n * ```\n */\n async getJob(jobId: string): Promise<Job> {\n const response = await this.fetch(`${this.baseUrl}/jobs/${encodeURIComponent(jobId)}`, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n },\n });\n\n if (!response.ok) {\n await this.handleError(response);\n }\n\n return toJob(await response.json());\n }\n\n /**\n * Poll a video job until it is completed.\n *\n * @param job - A Job or a job ID\n * @param options - Poll interval, timeout and progress callback\n * @returns The completed job; `resultUrl` is a direct download link valid for 24 hours\n * @throws PoofError with code `job_failed` if the job fails (credits are refunded),\n * or code `timeout` if `timeoutMs` elapses first\n *\n * @example\n * ```typescript\n * const done = await poof.waitForJob(job, {\n * onProgress: (j) => console.log(`${Math.round(j.progress * 100)}%`),\n * });\n * const video = await fetch(done.resultUrl!);\n * ```\n */\n async waitForJob(job: Job | string, options: WaitForJobOptions = {}): Promise<Job> {\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const timeoutMs = options.timeoutMs ?? DEFAULT_JOB_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n\n let current: Job = typeof job === 'string' ? await this.getJob(job) : job;\n options.onProgress?.(current);\n\n while (!isTerminal(current.status)) {\n if (Date.now() >= deadline) {\n throw new PoofError(\n `Timed out after ${timeoutMs}ms waiting for job ${current.id}`,\n 'timeout',\n { status: 408 }\n );\n }\n await sleep(Math.min(pollIntervalMs, Math.max(0, deadline - Date.now())));\n current = await this.getJob(current.id);\n options.onProgress?.(current);\n }\n\n if (current.status === 'failed') {\n throw new PoofError(\n current.error?.message || `Job ${current.id} failed`,\n 'job_failed',\n { details: current.error?.code }\n );\n }\n\n return current;\n }\n\n /**\n * Get account information\n *\n * @returns Account info including plan and credit usage\n *\n * @example\n * ```typescript\n * const account = await poof.me();\n * console.log(`Plan: ${account.plan}`);\n * console.log(`Credits: ${account.usedCredits}/${account.maxCredits}`);\n * ```\n */\n async me(): Promise<AccountInfo> {\n const response = await this.fetch(`${this.baseUrl}/me`, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n },\n });\n\n if (!response.ok) {\n await this.handleError(response);\n }\n\n return response.json();\n }\n\n /** Make a fetch request with auth and timeout */\n private async fetch(url: string, init: RequestInit): Promise<Response> {\n const headers = new Headers(init.headers);\n headers.set('x-api-key', this.apiKey);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n ...init,\n headers,\n signal: controller.signal,\n });\n return response;\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new PoofError('Request timeout', 'timeout', { status: 408 });\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /** Handle error response */\n private async handleError(response: Response): Promise<never> {\n let errorData: ApiErrorResponse;\n\n try {\n errorData = await response.json();\n } catch {\n throw new PoofError(\n `HTTP ${response.status}: ${response.statusText}`,\n 'unknown_error',\n { status: response.status }\n );\n }\n\n throw PoofError.fromResponse(errorData, response.status);\n }\n}\n\n/**\n * Type guard: did removeBackground() return a video Job rather than image bytes?\n *\n * @example\n * ```typescript\n * const result = await poof.removeBackground(input);\n * if (isJob(result)) {\n * const done = await poof.waitForJob(result);\n * } else {\n * fs.writeFileSync('output.png', Buffer.from(result.data));\n * }\n * ```\n */\nexport function isJob(value: RemoveBackgroundResult | Job): value is Job {\n return typeof value === 'object' && value !== null && 'status' in value && typeof (value as Job).id === 'string';\n}\n\nfunction isTerminal(status: JobStatus): boolean {\n return status === 'completed' || status === 'failed';\n}\n\n/** The job object as the API returns it (snake_case) */\ninterface RawJob {\n id: string;\n status: JobStatus;\n progress: number;\n created_at: string;\n started_at: string | null;\n completed_at: string | null;\n duration_seconds: number;\n fps: number;\n width: number;\n height: number;\n frames: number;\n credits: number;\n format: Job['format'];\n channels: Job['channels'];\n status_url: string;\n result_url: string | null;\n expires_at: string | null;\n error: Job['error'];\n stats: Job['stats'];\n}\n\nfunction toJob(raw: RawJob): Job {\n return {\n id: raw.id,\n status: raw.status,\n progress: raw.progress ?? 0,\n createdAt: raw.created_at,\n startedAt: raw.started_at ?? null,\n completedAt: raw.completed_at ?? null,\n durationSeconds: raw.duration_seconds,\n fps: raw.fps,\n width: raw.width,\n height: raw.height,\n frames: raw.frames,\n credits: raw.credits,\n format: raw.format,\n channels: raw.channels,\n statusUrl: raw.status_url,\n resultUrl: raw.result_url ?? null,\n expiresAt: raw.expires_at ?? null,\n error: raw.error ?? null,\n stats: raw.stats ?? null,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poof-bg/js",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Official Poof SDK for background removal",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Official Poof SDK for image and video background removal",
|
|
5
5
|
"author": "Poof <support@poof.bg>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://poof.bg",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/poof-bg/js"
|
|
10
|
+
"url": "git+https://github.com/poof-bg/js.git"
|
|
11
11
|
},
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/poof-bg/js/issues"
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
"keywords": [
|
|
16
16
|
"poof",
|
|
17
17
|
"background-removal",
|
|
18
|
+
"video-background-removal",
|
|
18
19
|
"image-processing",
|
|
20
|
+
"video",
|
|
19
21
|
"ai",
|
|
20
22
|
"sdk"
|
|
21
23
|
],
|
|
@@ -47,11 +49,13 @@
|
|
|
47
49
|
"build": "tsup",
|
|
48
50
|
"dev": "tsup --watch",
|
|
49
51
|
"typecheck": "tsc --noEmit",
|
|
52
|
+
"test": "vitest run",
|
|
50
53
|
"prepublishOnly": "npm run build"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
56
|
"@types/node": "^25.2.0",
|
|
54
57
|
"tsup": "^8.0.0",
|
|
55
|
-
"typescript": "^5.3.0"
|
|
58
|
+
"typescript": "^5.3.0",
|
|
59
|
+
"vitest": "^3.2.7"
|
|
56
60
|
}
|
|
57
61
|
}
|