bun-image-turbo 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
  </p>
24
24
 
25
25
  <p align="center">
26
- <b>Up to 950x faster</b> than alternatives • <b>Native HEIC support</b> • <b>Zero-copy buffers</b> • <b>SIMD-accelerated</b>
26
+ <b>Up to 950x faster</b> than alternatives • <b>Native HEIC support</b> • <b>EXIF metadata writing</b> • <b>SIMD-accelerated</b>
27
27
  </p>
28
28
 
29
29
  ---
@@ -34,8 +34,10 @@
34
34
  |----------|:-------:|-----|
35
35
  | WebP Metadata | **950x** | Header-only parsing, no decode |
36
36
  | JPEG Metadata | **38x** | Optimized marker extraction |
37
+ | WebP Resize | **1.25x** | Shrink-on-load optimization (NEW in v1.4.0) |
37
38
  | 50 Concurrent Ops | **2.6x** | Rayon thread pool |
38
39
  | Transform Pipeline | **1.6x** | Single-pass processing |
40
+ | EXIF Write | **<0.3ms** | Native EXIF embedding |
39
41
  | HEIC Support | **Exclusive** | Only lib with native HEIC |
40
42
 
41
43
  > **bun-image-turbo** is the fastest image processing library for JavaScript. Period.
@@ -81,7 +83,7 @@ Prebuilt binaries are available for **all major platforms** - no compilation nee
81
83
  ## Quick Start
82
84
 
83
85
  ```typescript
84
- import { metadata, resize, transform, toWebp, blurhash } from 'bun-image-turbo';
86
+ import { metadata, resize, transform, toWebp, blurhash, writeExif } from 'bun-image-turbo';
85
87
 
86
88
  // Read image
87
89
  const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
@@ -108,8 +110,16 @@ const result = await transform(buffer, {
108
110
  // Generate blurhash placeholder
109
111
  const { hash } = await blurhash(buffer, 4, 3);
110
112
 
113
+ // Add EXIF metadata (perfect for AI images)
114
+ const withExif = await writeExif(webp, {
115
+ imageDescription: 'AI-generated sunset',
116
+ artist: 'Stable Diffusion XL',
117
+ software: 'ComfyUI',
118
+ userComment: JSON.stringify({ prompt: '...', seed: 12345 })
119
+ });
120
+
111
121
  // Save result
112
- await Bun.write('output.webp', result);
122
+ await Bun.write('output.webp', withExif);
113
123
  ```
114
124
 
115
125
  **[See more examples →](https://nexus-aissam.github.io/bun-image-turbo/examples/)**
@@ -137,6 +147,18 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
137
147
  | 1MB JPEG → 800px | **12.6ms** | 20.3ms | **1.6x** |
138
148
  | Thumbnail (200px) | **8.8ms** | 10.7ms | **1.2x** |
139
149
 
150
+ ### WebP Resize (NEW in v1.4.0)
151
+
152
+ | Source Size | Target | bun-image-turbo | sharp | Speedup |
153
+ |-------------|--------|---------------:|------:|:-------:|
154
+ | 800x600 | 200px | **3.1ms** | 4.3ms | **1.40x** |
155
+ | 1600x1200 | 200px | **6.4ms** | 8.0ms | **1.24x** |
156
+ | 2000x1500 | 200px | **8.6ms** | 10.1ms | **1.18x** |
157
+ | 3000x2000 | 200px | **14.7ms** | 16.1ms | **1.10x** |
158
+ | 4000x3000 | 400px | **32.4ms** | 33.1ms | **1.02x** |
159
+
160
+ > **v1.4.0** introduces WebP shrink-on-load optimization using libwebp's native scaling, making WebP resize operations **1.02-1.40x faster** than sharp across all sizes.
161
+
140
162
  ### HEIC Support (Exclusive)
141
163
 
142
164
  | Operation | Time | Notes |
@@ -148,6 +170,16 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
148
170
 
149
171
  > **sharp does NOT support HEIC/HEIF files!** bun-image-turbo is the only high-performance library with native HEIC support.
150
172
 
173
+ ### EXIF Metadata (NEW in v1.3.0)
174
+
175
+ | Operation | JPEG | WebP | Notes |
176
+ |-----------|-----:|-----:|:------|
177
+ | writeExif (simple) | **0.24ms** | **0.10ms** | 3 fields |
178
+ | writeExif (full) | **0.20ms** | **0.05ms** | 10 fields + JSON |
179
+ | stripExif | **0.26ms** | **0.08ms** | Remove all metadata |
180
+
181
+ > **Perfect for AI-generated images!** Store prompts, seeds, and generation parameters in standard EXIF fields.
182
+
151
183
  **[Full benchmark details →](https://nexus-aissam.github.io/bun-image-turbo/guide/performance)**
152
184
 
153
185
  ---
@@ -169,11 +201,14 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
169
201
  ## Features
170
202
 
171
203
  - **TurboJPEG with SIMD** - 2-6x faster JPEG encoding/decoding via libjpeg-turbo
172
- - **Shrink-on-Decode** - Decode JPEG/HEIC at reduced resolution for faster thumbnails
204
+ - **Shrink-on-Decode** - Decode JPEG/WebP/HEIC at reduced resolution for faster thumbnails
205
+ - **WebP Shrink-on-Load** - NEW in v1.4.0: Native libwebp scaling for 1.25x faster WebP resize
173
206
  - **Adaptive Algorithms** - Auto-selects optimal resize filter based on scale factor
174
207
  - **Native HEIC Support** - The only high-performance library with HEIC/HEIF decoding
208
+ - **EXIF Metadata Writing** - Write/strip EXIF data for AI image attribution
175
209
  - **Blurhash Generation** - Built-in compact placeholder generation
176
210
  - **Multi-Step Resize** - Progressive halving for large scale reductions
211
+ - **Zero-Copy Optimizations** - Minimal memory copies for lower memory usage
177
212
  - **Async & Sync APIs** - Both async and sync versions available
178
213
  - **TypeScript First** - Full TypeScript support with strict types
179
214
  - **Cross-Platform** - macOS, Linux, Windows support
@@ -191,8 +226,10 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
191
226
  | `toPng()` | Convert to PNG | [→](https://nexus-aissam.github.io/bun-image-turbo/api/to-png) |
192
227
  | `toWebp()` | Convert to WebP | [→](https://nexus-aissam.github.io/bun-image-turbo/api/to-webp) |
193
228
  | `blurhash()` | Generate placeholder hash | [→](https://nexus-aissam.github.io/bun-image-turbo/api/blurhash) |
229
+ | `writeExif()` | Write EXIF metadata | [→](https://nexus-aissam.github.io/bun-image-turbo/api/exif) |
230
+ | `stripExif()` | Remove EXIF metadata | [→](https://nexus-aissam.github.io/bun-image-turbo/api/exif) |
194
231
 
195
- All functions have sync variants (`metadataSync`, `resizeSync`, etc.)
232
+ All functions have sync variants (`metadataSync`, `resizeSync`, `writeExifSync`, etc.)
196
233
 
197
234
  **[Full API Reference →](https://nexus-aissam.github.io/bun-image-turbo/api/)**
198
235
 
@@ -253,6 +290,29 @@ bun run batch # Parallel batch processing
253
290
  | [api-endpoint.ts](./examples/api-endpoint.ts) | HTTP image processing server |
254
291
  | [batch-processing.ts](./examples/batch-processing.ts) | Parallel multi-file processing |
255
292
 
293
+ ### EXIF Metadata Example
294
+
295
+ ```typescript
296
+ import { writeExif, toWebp, stripExif } from 'bun-image-turbo';
297
+
298
+ // Add AI generation metadata to WebP
299
+ const webp = await toWebp(imageBuffer, { quality: 90 });
300
+ const withMetadata = await writeExif(webp, {
301
+ imageDescription: 'A sunset over mountains',
302
+ artist: 'Stable Diffusion XL',
303
+ software: 'ComfyUI',
304
+ userComment: JSON.stringify({
305
+ prompt: 'sunset over mountains, 8k',
306
+ seed: 12345,
307
+ steps: 30,
308
+ cfg_scale: 7.5
309
+ })
310
+ });
311
+
312
+ // Strip metadata for privacy
313
+ const clean = await stripExif(photoWithGPS);
314
+ ```
315
+
256
316
  **[All examples →](https://nexus-aissam.github.io/bun-image-turbo/examples/)**
257
317
 
258
318
  ---
@@ -346,6 +406,7 @@ MIT License - see [LICENSE](LICENSE) for details.
346
406
  - [fast_image_resize](https://crates.io/crates/fast_image_resize) - Fast image resizing with Rayon
347
407
  - [webp](https://crates.io/crates/webp) - WebP encoding/decoding
348
408
  - [libheif-rs](https://crates.io/crates/libheif-rs) - HEIC/HEIF decoding via libheif
409
+ - [img-parts](https://crates.io/crates/img-parts) - EXIF/XMP metadata manipulation
349
410
  - [blurhash](https://crates.io/crates/blurhash) - Blurhash generation
350
411
  - [napi-rs](https://napi.rs/) - Rust bindings for Node.js
351
412
 
package/dist/index.d.mts CHANGED
@@ -107,6 +107,29 @@ interface BlurHashResult {
107
107
  /** Original height */
108
108
  height: number;
109
109
  }
110
+ /** EXIF metadata options for writing */
111
+ interface ExifOptions {
112
+ /** Image description / caption / AI prompt */
113
+ imageDescription?: string;
114
+ /** Artist / creator name */
115
+ artist?: string;
116
+ /** Copyright notice */
117
+ copyright?: string;
118
+ /** Software used to create the image */
119
+ software?: string;
120
+ /** Date/time in EXIF format (YYYY:MM:DD HH:MM:SS) */
121
+ dateTime?: string;
122
+ /** Original date/time in EXIF format */
123
+ dateTimeOriginal?: string;
124
+ /** User comment (can contain JSON or other data) */
125
+ userComment?: string;
126
+ /** Camera/device make */
127
+ make?: string;
128
+ /** Camera/device model */
129
+ model?: string;
130
+ /** Orientation (1-8) */
131
+ orientation?: number;
132
+ }
110
133
  /** Transform options (all-in-one processing) */
111
134
  interface TransformOptions {
112
135
  /** Resize options */
@@ -129,31 +152,12 @@ interface TransformOptions {
129
152
  brightness?: number;
130
153
  /** Contrast adjustment (-100 to 100) */
131
154
  contrast?: number;
155
+ /** EXIF metadata to write (for JPEG/WebP output) */
156
+ exif?: ExifOptions;
132
157
  }
133
158
 
134
159
  /**
135
- * bun-image-turbo - High-performance image processing for Bun and Node.js
136
- *
137
- * @module bun-image-turbo
138
- * @author Aissam Irhir <aissamirhir@gmail.com>
139
- *
140
- * @example
141
- * ```typescript
142
- * import { resize, toWebp, metadata } from 'bun-image-turbo';
143
- *
144
- * // Read image
145
- * const input = await Bun.file('input.jpg').arrayBuffer();
146
- *
147
- * // Resize image
148
- * const resized = await resize(Buffer.from(input), { width: 800, height: 600 });
149
- *
150
- * // Convert to WebP
151
- * const webp = await toWebp(Buffer.from(input), { quality: 85 });
152
- *
153
- * // Get metadata
154
- * const info = await metadata(Buffer.from(input));
155
- * console.log(info.width, info.height, info.format);
156
- * ```
160
+ * Metadata API functions
157
161
  */
158
162
 
159
163
  /**
@@ -169,6 +173,15 @@ interface TransformOptions {
169
173
  * ```
170
174
  */
171
175
  declare function metadata(input: Buffer): Promise<ImageMetadata>;
176
+ /**
177
+ * Get image metadata synchronously
178
+ */
179
+ declare function metadataSync(input: Buffer): ImageMetadata;
180
+
181
+ /**
182
+ * Resize API functions
183
+ */
184
+
172
185
  /**
173
186
  * Resize image asynchronously
174
187
  *
@@ -193,6 +206,15 @@ declare function metadata(input: Buffer): Promise<ImageMetadata>;
193
206
  * ```
194
207
  */
195
208
  declare function resize(input: Buffer, options: ResizeOptions): Promise<Buffer>;
209
+ /**
210
+ * Resize image synchronously
211
+ */
212
+ declare function resizeSync(input: Buffer, options: ResizeOptions): Buffer;
213
+
214
+ /**
215
+ * Image encoding/format conversion API functions
216
+ */
217
+
196
218
  /**
197
219
  * Convert image to JPEG asynchronously
198
220
  *
@@ -206,6 +228,10 @@ declare function resize(input: Buffer, options: ResizeOptions): Promise<Buffer>;
206
228
  * ```
207
229
  */
208
230
  declare function toJpeg(input: Buffer, options?: JpegOptions): Promise<Buffer>;
231
+ /**
232
+ * Convert image to JPEG synchronously
233
+ */
234
+ declare function toJpegSync(input: Buffer, options?: JpegOptions): Buffer;
209
235
  /**
210
236
  * Convert image to PNG asynchronously
211
237
  *
@@ -219,6 +245,10 @@ declare function toJpeg(input: Buffer, options?: JpegOptions): Promise<Buffer>;
219
245
  * ```
220
246
  */
221
247
  declare function toPng(input: Buffer, options?: PngOptions): Promise<Buffer>;
248
+ /**
249
+ * Convert image to PNG synchronously
250
+ */
251
+ declare function toPngSync(input: Buffer, options?: PngOptions): Buffer;
222
252
  /**
223
253
  * Convert image to WebP asynchronously
224
254
  *
@@ -236,6 +266,15 @@ declare function toPng(input: Buffer, options?: PngOptions): Promise<Buffer>;
236
266
  * ```
237
267
  */
238
268
  declare function toWebp(input: Buffer, options?: WebPOptions): Promise<Buffer>;
269
+ /**
270
+ * Convert image to WebP synchronously
271
+ */
272
+ declare function toWebpSync(input: Buffer, options?: WebPOptions): Buffer;
273
+
274
+ /**
275
+ * Transform API functions
276
+ */
277
+
239
278
  /**
240
279
  * Transform image with multiple operations asynchronously
241
280
  *
@@ -257,6 +296,15 @@ declare function toWebp(input: Buffer, options?: WebPOptions): Promise<Buffer>;
257
296
  * ```
258
297
  */
259
298
  declare function transform(input: Buffer, options: TransformOptions): Promise<Buffer>;
299
+ /**
300
+ * Transform image with multiple operations synchronously
301
+ */
302
+ declare function transformSync(input: Buffer, options: TransformOptions): Buffer;
303
+
304
+ /**
305
+ * Blurhash API functions
306
+ */
307
+
260
308
  /**
261
309
  * Generate blurhash from image asynchronously
262
310
  *
@@ -275,37 +323,91 @@ declare function transform(input: Buffer, options: TransformOptions): Promise<Bu
275
323
  */
276
324
  declare function blurhash(input: Buffer, componentsX?: number, componentsY?: number): Promise<BlurHashResult>;
277
325
  /**
278
- * Get image metadata synchronously
326
+ * Generate blurhash from image synchronously
279
327
  */
280
- declare function metadataSync(input: Buffer): ImageMetadata;
328
+ declare function blurhashSync(input: Buffer, componentsX?: number, componentsY?: number): BlurHashResult;
329
+
281
330
  /**
282
- * Resize image synchronously
331
+ * EXIF metadata API functions
283
332
  */
284
- declare function resizeSync(input: Buffer, options: ResizeOptions): Buffer;
333
+
285
334
  /**
286
- * Convert image to JPEG synchronously
335
+ * Write EXIF metadata to an image asynchronously
336
+ *
337
+ * Supports JPEG and WebP formats.
338
+ *
339
+ * @param input - Image buffer
340
+ * @param options - EXIF metadata options
341
+ * @returns Promise resolving to image buffer with EXIF data
342
+ *
343
+ * @example
344
+ * ```typescript
345
+ * const withExif = await writeExif(imageBuffer, {
346
+ * imageDescription: 'A beautiful sunset over the ocean',
347
+ * artist: 'John Doe',
348
+ * software: 'My AI App v1.0',
349
+ * userComment: JSON.stringify({ model: 'stable-diffusion', seed: 12345 })
350
+ * });
351
+ * ```
287
352
  */
288
- declare function toJpegSync(input: Buffer, options?: JpegOptions): Buffer;
353
+ declare function writeExif(input: Buffer, options: ExifOptions): Promise<Buffer>;
289
354
  /**
290
- * Convert image to PNG synchronously
355
+ * Write EXIF metadata to an image synchronously
356
+ *
357
+ * Supports JPEG and WebP formats.
291
358
  */
292
- declare function toPngSync(input: Buffer, options?: PngOptions): Buffer;
359
+ declare function writeExifSync(input: Buffer, options: ExifOptions): Buffer;
293
360
  /**
294
- * Convert image to WebP synchronously
361
+ * Strip all EXIF metadata from an image asynchronously
362
+ *
363
+ * Supports JPEG and WebP formats.
364
+ *
365
+ * @param input - Image buffer
366
+ * @returns Promise resolving to image buffer without EXIF data
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * const stripped = await stripExif(imageBuffer);
371
+ * ```
295
372
  */
296
- declare function toWebpSync(input: Buffer, options?: WebPOptions): Buffer;
373
+ declare function stripExif(input: Buffer): Promise<Buffer>;
297
374
  /**
298
- * Transform image with multiple operations synchronously
375
+ * Strip all EXIF metadata from an image synchronously
376
+ *
377
+ * Supports JPEG and WebP formats.
299
378
  */
300
- declare function transformSync(input: Buffer, options: TransformOptions): Buffer;
379
+ declare function stripExifSync(input: Buffer): Buffer;
380
+
301
381
  /**
302
- * Generate blurhash from image synchronously
382
+ * bun-image-turbo - High-performance image processing for Bun and Node.js
383
+ *
384
+ * @module bun-image-turbo
385
+ * @author Aissam Irhir <aissamirhir@gmail.com>
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * import { resize, toWebp, metadata } from 'bun-image-turbo';
390
+ *
391
+ * // Read image
392
+ * const input = await Bun.file('input.jpg').arrayBuffer();
393
+ *
394
+ * // Resize image
395
+ * const resized = await resize(Buffer.from(input), { width: 800, height: 600 });
396
+ *
397
+ * // Convert to WebP
398
+ * const webp = await toWebp(Buffer.from(input), { quality: 85 });
399
+ *
400
+ * // Get metadata
401
+ * const info = await metadata(Buffer.from(input));
402
+ * console.log(info.width, info.height, info.format);
403
+ * ```
303
404
  */
304
- declare function blurhashSync(input: Buffer, componentsX?: number, componentsY?: number): BlurHashResult;
405
+
305
406
  /**
306
407
  * Get library version
307
408
  */
308
409
  declare function version(): string;
410
+
309
411
  declare const _default: {
310
412
  metadata: typeof metadata;
311
413
  metadataSync: typeof metadataSync;
@@ -321,7 +423,11 @@ declare const _default: {
321
423
  transformSync: typeof transformSync;
322
424
  blurhash: typeof blurhash;
323
425
  blurhashSync: typeof blurhashSync;
426
+ writeExif: typeof writeExif;
427
+ writeExifSync: typeof writeExifSync;
428
+ stripExif: typeof stripExif;
429
+ stripExifSync: typeof stripExifSync;
324
430
  version: typeof version;
325
431
  };
326
432
 
327
- export { type AvifOptions, type BlurHashResult, type FitMode, type ImageFormat, type ImageMetadata, type JpegOptions, type OutputOptions, type PngOptions, type ResizeFilter, type ResizeOptions, type TransformOptions, type WebPOptions, blurhash, blurhashSync, _default as default, metadata, metadataSync, resize, resizeSync, toJpeg, toJpegSync, toPng, toPngSync, toWebp, toWebpSync, transform, transformSync, version };
433
+ export { type AvifOptions, type BlurHashResult, type ExifOptions, type FitMode, type ImageFormat, type ImageMetadata, type JpegOptions, type OutputOptions, type PngOptions, type ResizeFilter, type ResizeOptions, type TransformOptions, type WebPOptions, blurhash, blurhashSync, _default as default, metadata, metadataSync, resize, resizeSync, stripExif, stripExifSync, toJpeg, toJpegSync, toPng, toPngSync, toWebp, toWebpSync, transform, transformSync, version, writeExif, writeExifSync };
package/dist/index.d.ts CHANGED
@@ -107,6 +107,29 @@ interface BlurHashResult {
107
107
  /** Original height */
108
108
  height: number;
109
109
  }
110
+ /** EXIF metadata options for writing */
111
+ interface ExifOptions {
112
+ /** Image description / caption / AI prompt */
113
+ imageDescription?: string;
114
+ /** Artist / creator name */
115
+ artist?: string;
116
+ /** Copyright notice */
117
+ copyright?: string;
118
+ /** Software used to create the image */
119
+ software?: string;
120
+ /** Date/time in EXIF format (YYYY:MM:DD HH:MM:SS) */
121
+ dateTime?: string;
122
+ /** Original date/time in EXIF format */
123
+ dateTimeOriginal?: string;
124
+ /** User comment (can contain JSON or other data) */
125
+ userComment?: string;
126
+ /** Camera/device make */
127
+ make?: string;
128
+ /** Camera/device model */
129
+ model?: string;
130
+ /** Orientation (1-8) */
131
+ orientation?: number;
132
+ }
110
133
  /** Transform options (all-in-one processing) */
111
134
  interface TransformOptions {
112
135
  /** Resize options */
@@ -129,31 +152,12 @@ interface TransformOptions {
129
152
  brightness?: number;
130
153
  /** Contrast adjustment (-100 to 100) */
131
154
  contrast?: number;
155
+ /** EXIF metadata to write (for JPEG/WebP output) */
156
+ exif?: ExifOptions;
132
157
  }
133
158
 
134
159
  /**
135
- * bun-image-turbo - High-performance image processing for Bun and Node.js
136
- *
137
- * @module bun-image-turbo
138
- * @author Aissam Irhir <aissamirhir@gmail.com>
139
- *
140
- * @example
141
- * ```typescript
142
- * import { resize, toWebp, metadata } from 'bun-image-turbo';
143
- *
144
- * // Read image
145
- * const input = await Bun.file('input.jpg').arrayBuffer();
146
- *
147
- * // Resize image
148
- * const resized = await resize(Buffer.from(input), { width: 800, height: 600 });
149
- *
150
- * // Convert to WebP
151
- * const webp = await toWebp(Buffer.from(input), { quality: 85 });
152
- *
153
- * // Get metadata
154
- * const info = await metadata(Buffer.from(input));
155
- * console.log(info.width, info.height, info.format);
156
- * ```
160
+ * Metadata API functions
157
161
  */
158
162
 
159
163
  /**
@@ -169,6 +173,15 @@ interface TransformOptions {
169
173
  * ```
170
174
  */
171
175
  declare function metadata(input: Buffer): Promise<ImageMetadata>;
176
+ /**
177
+ * Get image metadata synchronously
178
+ */
179
+ declare function metadataSync(input: Buffer): ImageMetadata;
180
+
181
+ /**
182
+ * Resize API functions
183
+ */
184
+
172
185
  /**
173
186
  * Resize image asynchronously
174
187
  *
@@ -193,6 +206,15 @@ declare function metadata(input: Buffer): Promise<ImageMetadata>;
193
206
  * ```
194
207
  */
195
208
  declare function resize(input: Buffer, options: ResizeOptions): Promise<Buffer>;
209
+ /**
210
+ * Resize image synchronously
211
+ */
212
+ declare function resizeSync(input: Buffer, options: ResizeOptions): Buffer;
213
+
214
+ /**
215
+ * Image encoding/format conversion API functions
216
+ */
217
+
196
218
  /**
197
219
  * Convert image to JPEG asynchronously
198
220
  *
@@ -206,6 +228,10 @@ declare function resize(input: Buffer, options: ResizeOptions): Promise<Buffer>;
206
228
  * ```
207
229
  */
208
230
  declare function toJpeg(input: Buffer, options?: JpegOptions): Promise<Buffer>;
231
+ /**
232
+ * Convert image to JPEG synchronously
233
+ */
234
+ declare function toJpegSync(input: Buffer, options?: JpegOptions): Buffer;
209
235
  /**
210
236
  * Convert image to PNG asynchronously
211
237
  *
@@ -219,6 +245,10 @@ declare function toJpeg(input: Buffer, options?: JpegOptions): Promise<Buffer>;
219
245
  * ```
220
246
  */
221
247
  declare function toPng(input: Buffer, options?: PngOptions): Promise<Buffer>;
248
+ /**
249
+ * Convert image to PNG synchronously
250
+ */
251
+ declare function toPngSync(input: Buffer, options?: PngOptions): Buffer;
222
252
  /**
223
253
  * Convert image to WebP asynchronously
224
254
  *
@@ -236,6 +266,15 @@ declare function toPng(input: Buffer, options?: PngOptions): Promise<Buffer>;
236
266
  * ```
237
267
  */
238
268
  declare function toWebp(input: Buffer, options?: WebPOptions): Promise<Buffer>;
269
+ /**
270
+ * Convert image to WebP synchronously
271
+ */
272
+ declare function toWebpSync(input: Buffer, options?: WebPOptions): Buffer;
273
+
274
+ /**
275
+ * Transform API functions
276
+ */
277
+
239
278
  /**
240
279
  * Transform image with multiple operations asynchronously
241
280
  *
@@ -257,6 +296,15 @@ declare function toWebp(input: Buffer, options?: WebPOptions): Promise<Buffer>;
257
296
  * ```
258
297
  */
259
298
  declare function transform(input: Buffer, options: TransformOptions): Promise<Buffer>;
299
+ /**
300
+ * Transform image with multiple operations synchronously
301
+ */
302
+ declare function transformSync(input: Buffer, options: TransformOptions): Buffer;
303
+
304
+ /**
305
+ * Blurhash API functions
306
+ */
307
+
260
308
  /**
261
309
  * Generate blurhash from image asynchronously
262
310
  *
@@ -275,37 +323,91 @@ declare function transform(input: Buffer, options: TransformOptions): Promise<Bu
275
323
  */
276
324
  declare function blurhash(input: Buffer, componentsX?: number, componentsY?: number): Promise<BlurHashResult>;
277
325
  /**
278
- * Get image metadata synchronously
326
+ * Generate blurhash from image synchronously
279
327
  */
280
- declare function metadataSync(input: Buffer): ImageMetadata;
328
+ declare function blurhashSync(input: Buffer, componentsX?: number, componentsY?: number): BlurHashResult;
329
+
281
330
  /**
282
- * Resize image synchronously
331
+ * EXIF metadata API functions
283
332
  */
284
- declare function resizeSync(input: Buffer, options: ResizeOptions): Buffer;
333
+
285
334
  /**
286
- * Convert image to JPEG synchronously
335
+ * Write EXIF metadata to an image asynchronously
336
+ *
337
+ * Supports JPEG and WebP formats.
338
+ *
339
+ * @param input - Image buffer
340
+ * @param options - EXIF metadata options
341
+ * @returns Promise resolving to image buffer with EXIF data
342
+ *
343
+ * @example
344
+ * ```typescript
345
+ * const withExif = await writeExif(imageBuffer, {
346
+ * imageDescription: 'A beautiful sunset over the ocean',
347
+ * artist: 'John Doe',
348
+ * software: 'My AI App v1.0',
349
+ * userComment: JSON.stringify({ model: 'stable-diffusion', seed: 12345 })
350
+ * });
351
+ * ```
287
352
  */
288
- declare function toJpegSync(input: Buffer, options?: JpegOptions): Buffer;
353
+ declare function writeExif(input: Buffer, options: ExifOptions): Promise<Buffer>;
289
354
  /**
290
- * Convert image to PNG synchronously
355
+ * Write EXIF metadata to an image synchronously
356
+ *
357
+ * Supports JPEG and WebP formats.
291
358
  */
292
- declare function toPngSync(input: Buffer, options?: PngOptions): Buffer;
359
+ declare function writeExifSync(input: Buffer, options: ExifOptions): Buffer;
293
360
  /**
294
- * Convert image to WebP synchronously
361
+ * Strip all EXIF metadata from an image asynchronously
362
+ *
363
+ * Supports JPEG and WebP formats.
364
+ *
365
+ * @param input - Image buffer
366
+ * @returns Promise resolving to image buffer without EXIF data
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * const stripped = await stripExif(imageBuffer);
371
+ * ```
295
372
  */
296
- declare function toWebpSync(input: Buffer, options?: WebPOptions): Buffer;
373
+ declare function stripExif(input: Buffer): Promise<Buffer>;
297
374
  /**
298
- * Transform image with multiple operations synchronously
375
+ * Strip all EXIF metadata from an image synchronously
376
+ *
377
+ * Supports JPEG and WebP formats.
299
378
  */
300
- declare function transformSync(input: Buffer, options: TransformOptions): Buffer;
379
+ declare function stripExifSync(input: Buffer): Buffer;
380
+
301
381
  /**
302
- * Generate blurhash from image synchronously
382
+ * bun-image-turbo - High-performance image processing for Bun and Node.js
383
+ *
384
+ * @module bun-image-turbo
385
+ * @author Aissam Irhir <aissamirhir@gmail.com>
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * import { resize, toWebp, metadata } from 'bun-image-turbo';
390
+ *
391
+ * // Read image
392
+ * const input = await Bun.file('input.jpg').arrayBuffer();
393
+ *
394
+ * // Resize image
395
+ * const resized = await resize(Buffer.from(input), { width: 800, height: 600 });
396
+ *
397
+ * // Convert to WebP
398
+ * const webp = await toWebp(Buffer.from(input), { quality: 85 });
399
+ *
400
+ * // Get metadata
401
+ * const info = await metadata(Buffer.from(input));
402
+ * console.log(info.width, info.height, info.format);
403
+ * ```
303
404
  */
304
- declare function blurhashSync(input: Buffer, componentsX?: number, componentsY?: number): BlurHashResult;
405
+
305
406
  /**
306
407
  * Get library version
307
408
  */
308
409
  declare function version(): string;
410
+
309
411
  declare const _default: {
310
412
  metadata: typeof metadata;
311
413
  metadataSync: typeof metadataSync;
@@ -321,7 +423,11 @@ declare const _default: {
321
423
  transformSync: typeof transformSync;
322
424
  blurhash: typeof blurhash;
323
425
  blurhashSync: typeof blurhashSync;
426
+ writeExif: typeof writeExif;
427
+ writeExifSync: typeof writeExifSync;
428
+ stripExif: typeof stripExif;
429
+ stripExifSync: typeof stripExifSync;
324
430
  version: typeof version;
325
431
  };
326
432
 
327
- export { type AvifOptions, type BlurHashResult, type FitMode, type ImageFormat, type ImageMetadata, type JpegOptions, type OutputOptions, type PngOptions, type ResizeFilter, type ResizeOptions, type TransformOptions, type WebPOptions, blurhash, blurhashSync, _default as default, metadata, metadataSync, resize, resizeSync, toJpeg, toJpegSync, toPng, toPngSync, toWebp, toWebpSync, transform, transformSync, version };
433
+ export { type AvifOptions, type BlurHashResult, type ExifOptions, type FitMode, type ImageFormat, type ImageMetadata, type JpegOptions, type OutputOptions, type PngOptions, type ResizeFilter, type ResizeOptions, type TransformOptions, type WebPOptions, blurhash, blurhashSync, _default as default, metadata, metadataSync, resize, resizeSync, stripExif, stripExifSync, toJpeg, toJpegSync, toPng, toPngSync, toWebp, toWebpSync, transform, transformSync, version, writeExif, writeExifSync };
package/dist/index.js CHANGED
@@ -27,6 +27,8 @@ __export(index_exports, {
27
27
  metadataSync: () => metadataSync,
28
28
  resize: () => resize,
29
29
  resizeSync: () => resizeSync,
30
+ stripExif: () => stripExif,
31
+ stripExifSync: () => stripExifSync,
30
32
  toJpeg: () => toJpeg,
31
33
  toJpegSync: () => toJpegSync,
32
34
  toPng: () => toPng,
@@ -35,20 +37,24 @@ __export(index_exports, {
35
37
  toWebpSync: () => toWebpSync,
36
38
  transform: () => transform,
37
39
  transformSync: () => transformSync,
38
- version: () => version
40
+ version: () => version,
41
+ writeExif: () => writeExif,
42
+ writeExifSync: () => writeExifSync
39
43
  });
40
44
  module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/loader.ts
41
47
  var import_fs = require("fs");
42
48
  var import_path = require("path");
43
49
  var import_url = require("url");
44
50
  var import_meta = {};
45
- var getCurrentDir = () => {
51
+ function getCurrentDir() {
46
52
  try {
47
53
  return (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
48
54
  } catch {
49
55
  return __dirname;
50
56
  }
51
- };
57
+ }
52
58
  function loadNativeBinding() {
53
59
  const platform = process.platform;
54
60
  const arch = process.arch;
@@ -102,6 +108,16 @@ function loadNativeBinding() {
102
108
  );
103
109
  }
104
110
  var native = loadNativeBinding();
111
+
112
+ // src/api/metadata.ts
113
+ async function metadata(input) {
114
+ return native.metadata(input);
115
+ }
116
+ function metadataSync(input) {
117
+ return native.metadataSync(input);
118
+ }
119
+
120
+ // src/converters.ts
105
121
  function toNapiFilter(filter) {
106
122
  if (!filter) return void 0;
107
123
  return filter.charAt(0).toUpperCase() + filter.slice(1);
@@ -154,50 +170,71 @@ function toNapiTransformOptions(options) {
154
170
  result.sharpen = options.sharpen;
155
171
  result.brightness = options.brightness;
156
172
  result.contrast = options.contrast;
173
+ if (options.exif) {
174
+ result.exif = options.exif;
175
+ }
157
176
  return result;
158
177
  }
159
- async function metadata(input) {
160
- return native.metadata(input);
161
- }
178
+
179
+ // src/api/resize.ts
162
180
  async function resize(input, options) {
163
181
  return native.resize(input, toNapiResizeOptions(options));
164
182
  }
183
+ function resizeSync(input, options) {
184
+ return native.resizeSync(input, toNapiResizeOptions(options));
185
+ }
186
+
187
+ // src/api/encode.ts
165
188
  async function toJpeg(input, options) {
166
189
  return native.toJpeg(input, options);
167
190
  }
191
+ function toJpegSync(input, options) {
192
+ return native.toJpegSync(input, options);
193
+ }
168
194
  async function toPng(input, options) {
169
195
  return native.toPng(input, options);
170
196
  }
197
+ function toPngSync(input, options) {
198
+ return native.toPngSync(input, options);
199
+ }
171
200
  async function toWebp(input, options) {
172
201
  return native.toWebp(input, options);
173
202
  }
203
+ function toWebpSync(input, options) {
204
+ return native.toWebpSync(input, options);
205
+ }
206
+
207
+ // src/api/transform.ts
174
208
  async function transform(input, options) {
175
209
  return native.transform(input, toNapiTransformOptions(options));
176
210
  }
211
+ function transformSync(input, options) {
212
+ return native.transformSync(input, toNapiTransformOptions(options));
213
+ }
214
+
215
+ // src/api/blurhash.ts
177
216
  async function blurhash(input, componentsX, componentsY) {
178
217
  return native.blurhash(input, componentsX, componentsY);
179
218
  }
180
- function metadataSync(input) {
181
- return native.metadataSync(input);
182
- }
183
- function resizeSync(input, options) {
184
- return native.resizeSync(input, toNapiResizeOptions(options));
185
- }
186
- function toJpegSync(input, options) {
187
- return native.toJpegSync(input, options);
219
+ function blurhashSync(input, componentsX, componentsY) {
220
+ return native.blurhashSync(input, componentsX, componentsY);
188
221
  }
189
- function toPngSync(input, options) {
190
- return native.toPngSync(input, options);
222
+
223
+ // src/api/exif.ts
224
+ async function writeExif(input, options) {
225
+ return native.writeExif(input, options);
191
226
  }
192
- function toWebpSync(input, options) {
193
- return native.toWebpSync(input, options);
227
+ function writeExifSync(input, options) {
228
+ return native.writeExifSync(input, options);
194
229
  }
195
- function transformSync(input, options) {
196
- return native.transformSync(input, toNapiTransformOptions(options));
230
+ async function stripExif(input) {
231
+ return native.stripExif(input);
197
232
  }
198
- function blurhashSync(input, componentsX, componentsY) {
199
- return native.blurhashSync(input, componentsX, componentsY);
233
+ function stripExifSync(input) {
234
+ return native.stripExifSync(input);
200
235
  }
236
+
237
+ // src/index.ts
201
238
  function version() {
202
239
  return native.version();
203
240
  }
@@ -216,6 +253,10 @@ var index_default = {
216
253
  transformSync,
217
254
  blurhash,
218
255
  blurhashSync,
256
+ writeExif,
257
+ writeExifSync,
258
+ stripExif,
259
+ stripExifSync,
219
260
  version
220
261
  };
221
262
  // Annotate the CommonJS export names for ESM import in node:
@@ -226,6 +267,8 @@ var index_default = {
226
267
  metadataSync,
227
268
  resize,
228
269
  resizeSync,
270
+ stripExif,
271
+ stripExifSync,
229
272
  toJpeg,
230
273
  toJpegSync,
231
274
  toPng,
@@ -234,5 +277,7 @@ var index_default = {
234
277
  toWebpSync,
235
278
  transform,
236
279
  transformSync,
237
- version
280
+ version,
281
+ writeExif,
282
+ writeExifSync
238
283
  });
package/dist/index.mjs CHANGED
@@ -5,17 +5,17 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
5
5
  throw Error('Dynamic require of "' + x + '" is not supported');
6
6
  });
7
7
 
8
- // src/index.ts
8
+ // src/loader.ts
9
9
  import { existsSync } from "fs";
10
10
  import { join, dirname } from "path";
11
11
  import { fileURLToPath } from "url";
12
- var getCurrentDir = () => {
12
+ function getCurrentDir() {
13
13
  try {
14
14
  return dirname(fileURLToPath(import.meta.url));
15
15
  } catch {
16
16
  return __dirname;
17
17
  }
18
- };
18
+ }
19
19
  function loadNativeBinding() {
20
20
  const platform = process.platform;
21
21
  const arch = process.arch;
@@ -69,6 +69,16 @@ function loadNativeBinding() {
69
69
  );
70
70
  }
71
71
  var native = loadNativeBinding();
72
+
73
+ // src/api/metadata.ts
74
+ async function metadata(input) {
75
+ return native.metadata(input);
76
+ }
77
+ function metadataSync(input) {
78
+ return native.metadataSync(input);
79
+ }
80
+
81
+ // src/converters.ts
72
82
  function toNapiFilter(filter) {
73
83
  if (!filter) return void 0;
74
84
  return filter.charAt(0).toUpperCase() + filter.slice(1);
@@ -121,50 +131,71 @@ function toNapiTransformOptions(options) {
121
131
  result.sharpen = options.sharpen;
122
132
  result.brightness = options.brightness;
123
133
  result.contrast = options.contrast;
134
+ if (options.exif) {
135
+ result.exif = options.exif;
136
+ }
124
137
  return result;
125
138
  }
126
- async function metadata(input) {
127
- return native.metadata(input);
128
- }
139
+
140
+ // src/api/resize.ts
129
141
  async function resize(input, options) {
130
142
  return native.resize(input, toNapiResizeOptions(options));
131
143
  }
144
+ function resizeSync(input, options) {
145
+ return native.resizeSync(input, toNapiResizeOptions(options));
146
+ }
147
+
148
+ // src/api/encode.ts
132
149
  async function toJpeg(input, options) {
133
150
  return native.toJpeg(input, options);
134
151
  }
152
+ function toJpegSync(input, options) {
153
+ return native.toJpegSync(input, options);
154
+ }
135
155
  async function toPng(input, options) {
136
156
  return native.toPng(input, options);
137
157
  }
158
+ function toPngSync(input, options) {
159
+ return native.toPngSync(input, options);
160
+ }
138
161
  async function toWebp(input, options) {
139
162
  return native.toWebp(input, options);
140
163
  }
164
+ function toWebpSync(input, options) {
165
+ return native.toWebpSync(input, options);
166
+ }
167
+
168
+ // src/api/transform.ts
141
169
  async function transform(input, options) {
142
170
  return native.transform(input, toNapiTransformOptions(options));
143
171
  }
172
+ function transformSync(input, options) {
173
+ return native.transformSync(input, toNapiTransformOptions(options));
174
+ }
175
+
176
+ // src/api/blurhash.ts
144
177
  async function blurhash(input, componentsX, componentsY) {
145
178
  return native.blurhash(input, componentsX, componentsY);
146
179
  }
147
- function metadataSync(input) {
148
- return native.metadataSync(input);
149
- }
150
- function resizeSync(input, options) {
151
- return native.resizeSync(input, toNapiResizeOptions(options));
152
- }
153
- function toJpegSync(input, options) {
154
- return native.toJpegSync(input, options);
180
+ function blurhashSync(input, componentsX, componentsY) {
181
+ return native.blurhashSync(input, componentsX, componentsY);
155
182
  }
156
- function toPngSync(input, options) {
157
- return native.toPngSync(input, options);
183
+
184
+ // src/api/exif.ts
185
+ async function writeExif(input, options) {
186
+ return native.writeExif(input, options);
158
187
  }
159
- function toWebpSync(input, options) {
160
- return native.toWebpSync(input, options);
188
+ function writeExifSync(input, options) {
189
+ return native.writeExifSync(input, options);
161
190
  }
162
- function transformSync(input, options) {
163
- return native.transformSync(input, toNapiTransformOptions(options));
191
+ async function stripExif(input) {
192
+ return native.stripExif(input);
164
193
  }
165
- function blurhashSync(input, componentsX, componentsY) {
166
- return native.blurhashSync(input, componentsX, componentsY);
194
+ function stripExifSync(input) {
195
+ return native.stripExifSync(input);
167
196
  }
197
+
198
+ // src/index.ts
168
199
  function version() {
169
200
  return native.version();
170
201
  }
@@ -183,6 +214,10 @@ var index_default = {
183
214
  transformSync,
184
215
  blurhash,
185
216
  blurhashSync,
217
+ writeExif,
218
+ writeExifSync,
219
+ stripExif,
220
+ stripExifSync,
186
221
  version
187
222
  };
188
223
  export {
@@ -193,6 +228,8 @@ export {
193
228
  metadataSync,
194
229
  resize,
195
230
  resizeSync,
231
+ stripExif,
232
+ stripExifSync,
196
233
  toJpeg,
197
234
  toJpegSync,
198
235
  toPng,
@@ -201,5 +238,7 @@ export {
201
238
  toWebpSync,
202
239
  transform,
203
240
  transformSync,
204
- version
241
+ version,
242
+ writeExif,
243
+ writeExifSync
205
244
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-image-turbo",
3
- "version": "1.2.2",
3
+ "version": "1.4.0",
4
4
  "author": "Aissam Irhir <aissamirhir@gmail.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -64,6 +64,15 @@
64
64
  "turbo"
65
65
  ],
66
66
  "license": "MIT",
67
+ "optionalDependencies": {
68
+ "bun-image-turbo-darwin-arm64": "1.4.0",
69
+ "bun-image-turbo-darwin-x64": "1.4.0",
70
+ "bun-image-turbo-linux-arm64-gnu": "1.4.0",
71
+ "bun-image-turbo-linux-x64-gnu": "1.4.0",
72
+ "bun-image-turbo-linux-x64-musl": "1.4.0",
73
+ "bun-image-turbo-win32-arm64-msvc": "1.4.0",
74
+ "bun-image-turbo-win32-x64-msvc": "1.4.0"
75
+ },
67
76
  "napi": {
68
77
  "name": "image-turbo",
69
78
  "binaryName": "image-turbo",
@@ -90,14 +99,5 @@
90
99
  "lint": "eslint src",
91
100
  "clean": "rm -rf dist *.node npm"
92
101
  },
93
- "types": "./dist/index.d.ts",
94
- "optionalDependencies": {
95
- "bun-image-turbo-darwin-arm64": "1.2.2",
96
- "bun-image-turbo-darwin-x64": "1.2.2",
97
- "bun-image-turbo-linux-arm64-gnu": "1.2.2",
98
- "bun-image-turbo-linux-x64-gnu": "1.2.2",
99
- "bun-image-turbo-linux-x64-musl": "1.2.2",
100
- "bun-image-turbo-win32-arm64-msvc": "1.2.2",
101
- "bun-image-turbo-win32-x64-msvc": "1.2.2"
102
- }
102
+ "types": "./dist/index.d.ts"
103
103
  }