@poof-bg/js 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Poof
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # @poof-bg/js
2
+
3
+ Official TypeScript/JavaScript SDK for the [Poof](https://poof.bg) background removal API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @poof-bg/js
9
+ # or
10
+ yarn add @poof-bg/js
11
+ # or
12
+ pnpm add @poof-bg/js
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```typescript
18
+ import { Poof } from '@poof-bg/js';
19
+
20
+ const poof = new Poof({ apiKey: 'your-api-key' });
21
+
22
+ // Remove background from an image
23
+ const result = await poof.removeBackground(imageFile);
24
+
25
+ // result.data is an ArrayBuffer containing the processed image
26
+ // result.metadata contains processing info
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Basic Background Removal
32
+
33
+ ```typescript
34
+ import { Poof } from '@poof-bg/js';
35
+
36
+ const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
37
+
38
+ // From File (browser)
39
+ const fileInput = document.querySelector('input[type="file"]');
40
+ const file = fileInput.files[0];
41
+ const result = await poof.removeBackground(file);
42
+
43
+ // Create a Blob for display
44
+ const blob = new Blob([result.data], { type: result.metadata.contentType });
45
+ const url = URL.createObjectURL(blob);
46
+ ```
47
+
48
+ ### With Options
49
+
50
+ ```typescript
51
+ const result = await poof.removeBackground(file, {
52
+ format: 'webp', // Output format: 'png' | 'jpg' | 'webp'
53
+ crop: true, // Crop to subject bounds
54
+ size: 'medium', // Size: 'full' | 'preview' | 'small' | 'medium' | 'large'
55
+ channels: 'rgb', // 'rgba' for transparency, 'rgb' for opaque
56
+ bgColor: '#ffffff', // Background color (when channels is 'rgb')
57
+ });
58
+ ```
59
+
60
+ ### Node.js: From File Path
61
+
62
+ ```typescript
63
+ import { Poof } from '@poof-bg/js';
64
+ import fs from 'node:fs';
65
+
66
+ const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
67
+
68
+ // From file path (Node.js only)
69
+ const result = await poof.removeBackground('./input.jpg');
70
+
71
+ // Save to file
72
+ fs.writeFileSync('output.png', Buffer.from(result.data));
73
+ ```
74
+
75
+ ### Node.js: From Buffer
76
+
77
+ ```typescript
78
+ import { Poof } from '@poof-bg/js';
79
+ import fs from 'node:fs';
80
+
81
+ const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
82
+
83
+ const imageBuffer = fs.readFileSync('./input.jpg');
84
+ const result = await poof.removeBackground(imageBuffer);
85
+
86
+ fs.writeFileSync('output.png', Buffer.from(result.data));
87
+ ```
88
+
89
+ ### Account Information
90
+
91
+ ```typescript
92
+ const account = await poof.me();
93
+
94
+ console.log(`Organization: ${account.organizationId}`);
95
+ console.log(`Plan: ${account.plan}`);
96
+ console.log(`Credits: ${account.usedCredits} / ${account.maxCredits}`);
97
+ ```
98
+
99
+ ### Processing Metadata
100
+
101
+ ```typescript
102
+ const result = await poof.removeBackground(file);
103
+
104
+ console.log(`Request ID: ${result.metadata.requestId}`);
105
+ console.log(`Processing time: ${result.metadata.processingTimeMs}ms`);
106
+ console.log(`Output size: ${result.metadata.width}x${result.metadata.height}`);
107
+ console.log(`Content type: ${result.metadata.contentType}`);
108
+ ```
109
+
110
+ ## Error Handling
111
+
112
+ The SDK provides typed error classes for different error scenarios:
113
+
114
+ ```typescript
115
+ import {
116
+ Poof,
117
+ PoofError,
118
+ AuthenticationError,
119
+ PaymentRequiredError,
120
+ RateLimitError,
121
+ ValidationError,
122
+ } from '@poof-bg/js';
123
+
124
+ try {
125
+ const result = await poof.removeBackground(file);
126
+ } catch (error) {
127
+ if (error instanceof AuthenticationError) {
128
+ console.error('Invalid API key');
129
+ } else if (error instanceof PaymentRequiredError) {
130
+ console.error('Insufficient credits');
131
+ } else if (error instanceof RateLimitError) {
132
+ console.error('Rate limit exceeded, retry later');
133
+ } else if (error instanceof ValidationError) {
134
+ console.error('Invalid input:', error.details);
135
+ } else if (error instanceof PoofError) {
136
+ console.error(`API error: ${error.message} (${error.code})`);
137
+ console.error(`Request ID: ${error.requestId}`);
138
+ } else {
139
+ throw error;
140
+ }
141
+ }
142
+ ```
143
+
144
+ ## Configuration
145
+
146
+ ```typescript
147
+ const poof = new Poof({
148
+ apiKey: 'your-api-key',
149
+ baseUrl: 'https://api.poof.bg/v1', // Optional: custom base URL
150
+ timeout: 60000, // Optional: request timeout in ms
151
+ });
152
+ ```
153
+
154
+ ## Supported Input Types
155
+
156
+ | Input Type | Browser | Node.js |
157
+ |------------|---------|---------|
158
+ | `File` | ✅ | ✅ |
159
+ | `Blob` | ✅ | ✅ |
160
+ | `ArrayBuffer` | ✅ | ✅ |
161
+ | `Uint8Array` | ✅ | ✅ |
162
+ | `Buffer` | ❌ | ✅ |
163
+ | File path (string) | ❌ | ✅ |
164
+
165
+ ## TypeScript
166
+
167
+ Full TypeScript support with exported types:
168
+
169
+ ```typescript
170
+ import type {
171
+ PoofOptions,
172
+ RemoveBackgroundOptions,
173
+ RemoveBackgroundResult,
174
+ ProcessingMetadata,
175
+ AccountInfo,
176
+ ImageFormat,
177
+ ImageSize,
178
+ Channels,
179
+ } from '@poof-bg/js';
180
+ ```
181
+
182
+ ## Requirements
183
+
184
+ - Node.js 18+ (uses native `fetch`)
185
+ - Modern browsers with `fetch` and `FormData` support
186
+
187
+ ## License
188
+
189
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,304 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var PoofError = class _PoofError extends Error {
5
+ /** Machine-readable error code */
6
+ code;
7
+ /** HTTP status code */
8
+ status;
9
+ /** Unique request ID for support */
10
+ requestId;
11
+ /** Additional details */
12
+ details;
13
+ constructor(message, code, options) {
14
+ super(message);
15
+ this.name = "PoofError";
16
+ this.code = code;
17
+ this.status = options?.status;
18
+ this.requestId = options?.requestId;
19
+ this.details = options?.details;
20
+ if ("captureStackTrace" in Error && typeof Error.captureStackTrace === "function") {
21
+ Error.captureStackTrace(this, this.constructor);
22
+ }
23
+ }
24
+ /** Create error from API response */
25
+ static fromResponse(response, status) {
26
+ const ErrorClass = ERROR_MAP[response.code] || _PoofError;
27
+ return new ErrorClass(response.message, response.code, {
28
+ status,
29
+ requestId: response.request_id,
30
+ details: response.details
31
+ });
32
+ }
33
+ };
34
+ var AuthenticationError = class extends PoofError {
35
+ constructor(message, code, options) {
36
+ super(message, code, options);
37
+ this.name = "AuthenticationError";
38
+ }
39
+ };
40
+ var PermissionError = class extends PoofError {
41
+ constructor(message, code, options) {
42
+ super(message, code, options);
43
+ this.name = "PermissionError";
44
+ }
45
+ };
46
+ var PaymentRequiredError = class extends PoofError {
47
+ constructor(message, code, options) {
48
+ super(message, code, options);
49
+ this.name = "PaymentRequiredError";
50
+ }
51
+ };
52
+ var RateLimitError = class extends PoofError {
53
+ constructor(message, code, options) {
54
+ super(message, code, options);
55
+ this.name = "RateLimitError";
56
+ }
57
+ };
58
+ var ValidationError = class extends PoofError {
59
+ constructor(message, code, options) {
60
+ super(message, code, options);
61
+ this.name = "ValidationError";
62
+ }
63
+ };
64
+ var ServerError = class extends PoofError {
65
+ constructor(message, code, options) {
66
+ super(message, code, options);
67
+ this.name = "ServerError";
68
+ }
69
+ };
70
+ var ERROR_MAP = {
71
+ authentication_error: AuthenticationError,
72
+ permission_denied: PermissionError,
73
+ payment_required: PaymentRequiredError,
74
+ rate_limit_exceeded: RateLimitError,
75
+ validation_error: ValidationError,
76
+ missing_image: ValidationError,
77
+ image_too_large: ValidationError,
78
+ upstream_error: ServerError,
79
+ internal_server_error: ServerError
80
+ };
81
+
82
+ // src/utils.ts
83
+ var isNode = typeof process !== "undefined" && process.versions?.node;
84
+ function detectMimeType(buffer) {
85
+ if (buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71) {
86
+ return "image/png";
87
+ }
88
+ if (buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) {
89
+ return "image/jpeg";
90
+ }
91
+ if (buffer[0] === 82 && buffer[1] === 73 && buffer[2] === 70 && buffer[3] === 70) {
92
+ if (buffer[8] === 87 && buffer[9] === 69 && buffer[10] === 66 && buffer[11] === 80) {
93
+ return "image/webp";
94
+ }
95
+ }
96
+ if (buffer[0] === 71 && buffer[1] === 73 && buffer[2] === 70) {
97
+ return "image/gif";
98
+ }
99
+ return "application/octet-stream";
100
+ }
101
+ function getExtension(mimeType) {
102
+ const map = {
103
+ "image/png": "png",
104
+ "image/jpeg": "jpg",
105
+ "image/webp": "webp",
106
+ "image/gif": "gif"
107
+ };
108
+ return map[mimeType] || "bin";
109
+ }
110
+ async function toBlob(input) {
111
+ if (input instanceof Blob) {
112
+ return input;
113
+ }
114
+ if (input instanceof ArrayBuffer) {
115
+ const uint8 = new Uint8Array(input);
116
+ const mimeType = detectMimeType(uint8);
117
+ return new Blob([input], { type: mimeType });
118
+ }
119
+ if (input instanceof Uint8Array) {
120
+ const mimeType = detectMimeType(input);
121
+ return new Blob([input], { type: mimeType });
122
+ }
123
+ if (isNode && Buffer.isBuffer(input)) {
124
+ const uint8 = new Uint8Array(input);
125
+ const mimeType = detectMimeType(uint8);
126
+ return new Blob([input], { type: mimeType });
127
+ }
128
+ if (typeof input === "string") {
129
+ if (!isNode) {
130
+ throw new Error("File paths are only supported in Node.js. Use a Blob or File in the browser.");
131
+ }
132
+ const fs = await import('fs/promises');
133
+ const path = await import('path');
134
+ const buffer = await fs.readFile(input);
135
+ const uint8 = new Uint8Array(buffer);
136
+ const mimeType = detectMimeType(uint8);
137
+ const ext = path.extname(input).toLowerCase().slice(1);
138
+ const mimeFromExt = {
139
+ png: "image/png",
140
+ jpg: "image/jpeg",
141
+ jpeg: "image/jpeg",
142
+ webp: "image/webp",
143
+ gif: "image/gif"
144
+ };
145
+ const finalMimeType = mimeFromExt[ext] || mimeType;
146
+ return new Blob([buffer], { type: finalMimeType });
147
+ }
148
+ throw new Error(`Unsupported input type: ${typeof input}`);
149
+ }
150
+ function getFilename(input, mimeType) {
151
+ if (typeof File !== "undefined" && input instanceof File) {
152
+ return input.name;
153
+ }
154
+ if (typeof input === "string" && isNode) {
155
+ const parts = input.split(/[/\\]/);
156
+ return parts[parts.length - 1] || `image.${getExtension(mimeType)}`;
157
+ }
158
+ return `image.${getExtension(mimeType)}`;
159
+ }
160
+
161
+ // src/client.ts
162
+ var DEFAULT_BASE_URL = "https://api.poof.bg/v1";
163
+ var DEFAULT_TIMEOUT = 6e4;
164
+ var Poof = class {
165
+ apiKey;
166
+ baseUrl;
167
+ timeout;
168
+ constructor(options) {
169
+ if (!options.apiKey) {
170
+ throw new Error("API key is required");
171
+ }
172
+ this.apiKey = options.apiKey;
173
+ this.baseUrl = options.baseUrl?.replace(/\/$/, "") || DEFAULT_BASE_URL;
174
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
175
+ }
176
+ /**
177
+ * Remove background from an image
178
+ *
179
+ * @param input - Image input: File, Blob, Buffer, ArrayBuffer, Uint8Array, or file path (Node.js)
180
+ * @param options - Processing options
181
+ * @returns Processed image data and metadata
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * // Basic usage
186
+ * const result = await poof.removeBackground(file);
187
+ * const imageBlob = new Blob([result.data], { type: result.metadata.contentType });
188
+ *
189
+ * // With options
190
+ * const result = await poof.removeBackground(file, {
191
+ * format: 'webp',
192
+ * crop: true,
193
+ * size: 'medium'
194
+ * });
195
+ *
196
+ * // Node.js: from file path
197
+ * const result = await poof.removeBackground('/path/to/image.png');
198
+ *
199
+ * // Node.js: save to file
200
+ * const fs = require('fs');
201
+ * fs.writeFileSync('output.png', Buffer.from(result.data));
202
+ * ```
203
+ */
204
+ async removeBackground(input, options = {}) {
205
+ const blob = await toBlob(input);
206
+ const filename = getFilename(input, blob.type);
207
+ const formData = new FormData();
208
+ formData.append("image_file", blob, filename);
209
+ if (options.format) formData.append("format", options.format);
210
+ if (options.channels) formData.append("channels", options.channels);
211
+ if (options.bgColor) formData.append("bg_color", options.bgColor);
212
+ if (options.size) formData.append("size", options.size);
213
+ if (options.crop !== void 0) formData.append("crop", String(options.crop));
214
+ const response = await this.fetch(`${this.baseUrl}/remove`, {
215
+ method: "POST",
216
+ body: formData
217
+ });
218
+ if (!response.ok) {
219
+ await this.handleError(response);
220
+ }
221
+ const data = await response.arrayBuffer();
222
+ return {
223
+ data,
224
+ metadata: {
225
+ requestId: response.headers.get("X-Request-ID") || "",
226
+ processingTimeMs: parseInt(response.headers.get("X-Processing-Time-Ms") || "0", 10),
227
+ width: parseInt(response.headers.get("X-Image-Width") || "0", 10),
228
+ height: parseInt(response.headers.get("X-Image-Height") || "0", 10),
229
+ contentType: response.headers.get("Content-Type") || "image/png"
230
+ }
231
+ };
232
+ }
233
+ /**
234
+ * Get account information
235
+ *
236
+ * @returns Account info including plan and credit usage
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * const account = await poof.me();
241
+ * console.log(`Plan: ${account.plan}`);
242
+ * console.log(`Credits: ${account.usedCredits}/${account.maxCredits}`);
243
+ * ```
244
+ */
245
+ async me() {
246
+ const response = await this.fetch(`${this.baseUrl}/me`, {
247
+ method: "GET",
248
+ headers: {
249
+ Accept: "application/json"
250
+ }
251
+ });
252
+ if (!response.ok) {
253
+ await this.handleError(response);
254
+ }
255
+ return response.json();
256
+ }
257
+ /** Make a fetch request with auth and timeout */
258
+ async fetch(url, init) {
259
+ const headers = new Headers(init.headers);
260
+ headers.set("x-api-key", this.apiKey);
261
+ const controller = new AbortController();
262
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
263
+ try {
264
+ const response = await fetch(url, {
265
+ ...init,
266
+ headers,
267
+ signal: controller.signal
268
+ });
269
+ return response;
270
+ } catch (error) {
271
+ if (error instanceof Error && error.name === "AbortError") {
272
+ throw new PoofError("Request timeout", "timeout", { status: 408 });
273
+ }
274
+ throw error;
275
+ } finally {
276
+ clearTimeout(timeoutId);
277
+ }
278
+ }
279
+ /** Handle error response */
280
+ async handleError(response) {
281
+ let errorData;
282
+ try {
283
+ errorData = await response.json();
284
+ } catch {
285
+ throw new PoofError(
286
+ `HTTP ${response.status}: ${response.statusText}`,
287
+ "unknown_error",
288
+ { status: response.status }
289
+ );
290
+ }
291
+ throw PoofError.fromResponse(errorData, response.status);
292
+ }
293
+ };
294
+
295
+ exports.AuthenticationError = AuthenticationError;
296
+ exports.PaymentRequiredError = PaymentRequiredError;
297
+ exports.PermissionError = PermissionError;
298
+ exports.Poof = Poof;
299
+ exports.PoofError = PoofError;
300
+ exports.RateLimitError = RateLimitError;
301
+ exports.ServerError = ServerError;
302
+ exports.ValidationError = ValidationError;
303
+ //# sourceMappingURL=index.cjs.map
304
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","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"]}