@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/dist/index.js ADDED
@@ -0,0 +1,295 @@
1
+ // src/errors.ts
2
+ var PoofError = class _PoofError extends Error {
3
+ /** Machine-readable error code */
4
+ code;
5
+ /** HTTP status code */
6
+ status;
7
+ /** Unique request ID for support */
8
+ requestId;
9
+ /** Additional details */
10
+ details;
11
+ constructor(message, code, options) {
12
+ super(message);
13
+ this.name = "PoofError";
14
+ this.code = code;
15
+ this.status = options?.status;
16
+ this.requestId = options?.requestId;
17
+ this.details = options?.details;
18
+ if ("captureStackTrace" in Error && typeof Error.captureStackTrace === "function") {
19
+ Error.captureStackTrace(this, this.constructor);
20
+ }
21
+ }
22
+ /** Create error from API response */
23
+ static fromResponse(response, status) {
24
+ const ErrorClass = ERROR_MAP[response.code] || _PoofError;
25
+ return new ErrorClass(response.message, response.code, {
26
+ status,
27
+ requestId: response.request_id,
28
+ details: response.details
29
+ });
30
+ }
31
+ };
32
+ var AuthenticationError = class extends PoofError {
33
+ constructor(message, code, options) {
34
+ super(message, code, options);
35
+ this.name = "AuthenticationError";
36
+ }
37
+ };
38
+ var PermissionError = class extends PoofError {
39
+ constructor(message, code, options) {
40
+ super(message, code, options);
41
+ this.name = "PermissionError";
42
+ }
43
+ };
44
+ var PaymentRequiredError = class extends PoofError {
45
+ constructor(message, code, options) {
46
+ super(message, code, options);
47
+ this.name = "PaymentRequiredError";
48
+ }
49
+ };
50
+ var RateLimitError = class extends PoofError {
51
+ constructor(message, code, options) {
52
+ super(message, code, options);
53
+ this.name = "RateLimitError";
54
+ }
55
+ };
56
+ var ValidationError = class extends PoofError {
57
+ constructor(message, code, options) {
58
+ super(message, code, options);
59
+ this.name = "ValidationError";
60
+ }
61
+ };
62
+ var ServerError = class extends PoofError {
63
+ constructor(message, code, options) {
64
+ super(message, code, options);
65
+ this.name = "ServerError";
66
+ }
67
+ };
68
+ var ERROR_MAP = {
69
+ authentication_error: AuthenticationError,
70
+ permission_denied: PermissionError,
71
+ payment_required: PaymentRequiredError,
72
+ rate_limit_exceeded: RateLimitError,
73
+ validation_error: ValidationError,
74
+ missing_image: ValidationError,
75
+ image_too_large: ValidationError,
76
+ upstream_error: ServerError,
77
+ internal_server_error: ServerError
78
+ };
79
+
80
+ // src/utils.ts
81
+ var isNode = typeof process !== "undefined" && process.versions?.node;
82
+ function detectMimeType(buffer) {
83
+ if (buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71) {
84
+ return "image/png";
85
+ }
86
+ if (buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) {
87
+ return "image/jpeg";
88
+ }
89
+ if (buffer[0] === 82 && buffer[1] === 73 && buffer[2] === 70 && buffer[3] === 70) {
90
+ if (buffer[8] === 87 && buffer[9] === 69 && buffer[10] === 66 && buffer[11] === 80) {
91
+ return "image/webp";
92
+ }
93
+ }
94
+ if (buffer[0] === 71 && buffer[1] === 73 && buffer[2] === 70) {
95
+ return "image/gif";
96
+ }
97
+ return "application/octet-stream";
98
+ }
99
+ function getExtension(mimeType) {
100
+ const map = {
101
+ "image/png": "png",
102
+ "image/jpeg": "jpg",
103
+ "image/webp": "webp",
104
+ "image/gif": "gif"
105
+ };
106
+ return map[mimeType] || "bin";
107
+ }
108
+ async function toBlob(input) {
109
+ if (input instanceof Blob) {
110
+ return input;
111
+ }
112
+ if (input instanceof ArrayBuffer) {
113
+ const uint8 = new Uint8Array(input);
114
+ const mimeType = detectMimeType(uint8);
115
+ return new Blob([input], { type: mimeType });
116
+ }
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 });
125
+ }
126
+ if (typeof input === "string") {
127
+ if (!isNode) {
128
+ throw new Error("File paths are only supported in Node.js. Use a Blob or File in the browser.");
129
+ }
130
+ const fs = await import('fs/promises');
131
+ const path = await import('path');
132
+ const buffer = await fs.readFile(input);
133
+ const uint8 = new Uint8Array(buffer);
134
+ const mimeType = detectMimeType(uint8);
135
+ const ext = path.extname(input).toLowerCase().slice(1);
136
+ const mimeFromExt = {
137
+ png: "image/png",
138
+ jpg: "image/jpeg",
139
+ jpeg: "image/jpeg",
140
+ webp: "image/webp",
141
+ gif: "image/gif"
142
+ };
143
+ const finalMimeType = mimeFromExt[ext] || mimeType;
144
+ return new Blob([buffer], { type: finalMimeType });
145
+ }
146
+ throw new Error(`Unsupported input type: ${typeof input}`);
147
+ }
148
+ function getFilename(input, mimeType) {
149
+ if (typeof File !== "undefined" && input instanceof File) {
150
+ return input.name;
151
+ }
152
+ if (typeof input === "string" && isNode) {
153
+ const parts = input.split(/[/\\]/);
154
+ return parts[parts.length - 1] || `image.${getExtension(mimeType)}`;
155
+ }
156
+ return `image.${getExtension(mimeType)}`;
157
+ }
158
+
159
+ // src/client.ts
160
+ var DEFAULT_BASE_URL = "https://api.poof.bg/v1";
161
+ var DEFAULT_TIMEOUT = 6e4;
162
+ var Poof = class {
163
+ apiKey;
164
+ baseUrl;
165
+ timeout;
166
+ constructor(options) {
167
+ if (!options.apiKey) {
168
+ throw new Error("API key is required");
169
+ }
170
+ this.apiKey = options.apiKey;
171
+ this.baseUrl = options.baseUrl?.replace(/\/$/, "") || DEFAULT_BASE_URL;
172
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
173
+ }
174
+ /**
175
+ * Remove background from an image
176
+ *
177
+ * @param input - Image input: File, Blob, Buffer, ArrayBuffer, Uint8Array, or file path (Node.js)
178
+ * @param options - Processing options
179
+ * @returns Processed image data and metadata
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // Basic usage
184
+ * const result = await poof.removeBackground(file);
185
+ * const imageBlob = new Blob([result.data], { type: result.metadata.contentType });
186
+ *
187
+ * // With options
188
+ * const result = await poof.removeBackground(file, {
189
+ * format: 'webp',
190
+ * crop: true,
191
+ * size: 'medium'
192
+ * });
193
+ *
194
+ * // Node.js: from file path
195
+ * const result = await poof.removeBackground('/path/to/image.png');
196
+ *
197
+ * // Node.js: save to file
198
+ * const fs = require('fs');
199
+ * fs.writeFileSync('output.png', Buffer.from(result.data));
200
+ * ```
201
+ */
202
+ async removeBackground(input, options = {}) {
203
+ const blob = await toBlob(input);
204
+ const filename = getFilename(input, blob.type);
205
+ const formData = new FormData();
206
+ formData.append("image_file", blob, filename);
207
+ if (options.format) formData.append("format", options.format);
208
+ if (options.channels) formData.append("channels", options.channels);
209
+ if (options.bgColor) formData.append("bg_color", options.bgColor);
210
+ if (options.size) formData.append("size", options.size);
211
+ if (options.crop !== void 0) formData.append("crop", String(options.crop));
212
+ const response = await this.fetch(`${this.baseUrl}/remove`, {
213
+ method: "POST",
214
+ body: formData
215
+ });
216
+ if (!response.ok) {
217
+ await this.handleError(response);
218
+ }
219
+ const data = await response.arrayBuffer();
220
+ return {
221
+ data,
222
+ metadata: {
223
+ requestId: response.headers.get("X-Request-ID") || "",
224
+ processingTimeMs: parseInt(response.headers.get("X-Processing-Time-Ms") || "0", 10),
225
+ width: parseInt(response.headers.get("X-Image-Width") || "0", 10),
226
+ height: parseInt(response.headers.get("X-Image-Height") || "0", 10),
227
+ contentType: response.headers.get("Content-Type") || "image/png"
228
+ }
229
+ };
230
+ }
231
+ /**
232
+ * Get account information
233
+ *
234
+ * @returns Account info including plan and credit usage
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * const account = await poof.me();
239
+ * console.log(`Plan: ${account.plan}`);
240
+ * console.log(`Credits: ${account.usedCredits}/${account.maxCredits}`);
241
+ * ```
242
+ */
243
+ async me() {
244
+ const response = await this.fetch(`${this.baseUrl}/me`, {
245
+ method: "GET",
246
+ headers: {
247
+ Accept: "application/json"
248
+ }
249
+ });
250
+ if (!response.ok) {
251
+ await this.handleError(response);
252
+ }
253
+ return response.json();
254
+ }
255
+ /** Make a fetch request with auth and timeout */
256
+ async fetch(url, init) {
257
+ const headers = new Headers(init.headers);
258
+ headers.set("x-api-key", this.apiKey);
259
+ const controller = new AbortController();
260
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
261
+ try {
262
+ const response = await fetch(url, {
263
+ ...init,
264
+ headers,
265
+ signal: controller.signal
266
+ });
267
+ return response;
268
+ } catch (error) {
269
+ if (error instanceof Error && error.name === "AbortError") {
270
+ throw new PoofError("Request timeout", "timeout", { status: 408 });
271
+ }
272
+ throw error;
273
+ } finally {
274
+ clearTimeout(timeoutId);
275
+ }
276
+ }
277
+ /** Handle error response */
278
+ async handleError(response) {
279
+ let errorData;
280
+ try {
281
+ errorData = await response.json();
282
+ } catch {
283
+ throw new PoofError(
284
+ `HTTP ${response.status}: ${response.statusText}`,
285
+ "unknown_error",
286
+ { status: response.status }
287
+ );
288
+ }
289
+ throw PoofError.fromResponse(errorData, response.status);
290
+ }
291
+ };
292
+
293
+ export { AuthenticationError, PaymentRequiredError, PermissionError, Poof, PoofError, RateLimitError, ServerError, ValidationError };
294
+ //# sourceMappingURL=index.js.map
295
+ //# sourceMappingURL=index.js.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.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"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@poof-bg/js",
3
+ "version": "1.0.0",
4
+ "description": "Official Poof SDK for background removal",
5
+ "author": "Poof <support@poof.bg>",
6
+ "license": "MIT",
7
+ "homepage": "https://poof.bg",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/poof-bg/js"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/poof-bg/js/issues"
14
+ },
15
+ "keywords": [
16
+ "poof",
17
+ "background-removal",
18
+ "image-processing",
19
+ "ai",
20
+ "sdk"
21
+ ],
22
+ "type": "module",
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "import": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "require": {
33
+ "types": "./dist/index.d.cts",
34
+ "default": "./dist/index.cjs"
35
+ }
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "dev": "tsup --watch",
49
+ "typecheck": "tsc --noEmit",
50
+ "prepublishOnly": "npm run build"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^25.2.0",
54
+ "tsup": "^8.0.0",
55
+ "typescript": "^5.3.0"
56
+ }
57
+ }