@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/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 mimeType = detectMimeType(input);
119
- return new Blob([input], { type: mimeType });
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] || `image.${getExtension(mimeType)}`;
173
+ return parts[parts.length - 1] || defaultFilename(mimeType);
155
174
  }
156
- return `image.${getExtension(mimeType)}`;
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 - Image input: File, Blob, Buffer, ArrayBuffer, Uint8Array, or file path (Node.js)
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
- * // Basic usage
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
- * // Node.js: from file path
195
- * const result = await poof.removeBackground('/path/to/image.png');
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
- * // Node.js: save to file
198
- * const fs = require('fs');
199
- * fs.writeFileSync('output.png', Buffer.from(result.data));
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
- formData.append("image_file", blob, filename);
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":"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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 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.0.0",
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
  }