bun-image-turbo 1.2.2 → 1.3.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
  ---
@@ -36,6 +36,7 @@
36
36
  | JPEG Metadata | **38x** | Optimized marker extraction |
37
37
  | 50 Concurrent Ops | **2.6x** | Rayon thread pool |
38
38
  | Transform Pipeline | **1.6x** | Single-pass processing |
39
+ | EXIF Write | **<0.3ms** | Native EXIF embedding |
39
40
  | HEIC Support | **Exclusive** | Only lib with native HEIC |
40
41
 
41
42
  > **bun-image-turbo** is the fastest image processing library for JavaScript. Period.
@@ -81,7 +82,7 @@ Prebuilt binaries are available for **all major platforms** - no compilation nee
81
82
  ## Quick Start
82
83
 
83
84
  ```typescript
84
- import { metadata, resize, transform, toWebp, blurhash } from 'bun-image-turbo';
85
+ import { metadata, resize, transform, toWebp, blurhash, writeExif } from 'bun-image-turbo';
85
86
 
86
87
  // Read image
87
88
  const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
@@ -108,8 +109,16 @@ const result = await transform(buffer, {
108
109
  // Generate blurhash placeholder
109
110
  const { hash } = await blurhash(buffer, 4, 3);
110
111
 
112
+ // Add EXIF metadata (perfect for AI images)
113
+ const withExif = await writeExif(webp, {
114
+ imageDescription: 'AI-generated sunset',
115
+ artist: 'Stable Diffusion XL',
116
+ software: 'ComfyUI',
117
+ userComment: JSON.stringify({ prompt: '...', seed: 12345 })
118
+ });
119
+
111
120
  // Save result
112
- await Bun.write('output.webp', result);
121
+ await Bun.write('output.webp', withExif);
113
122
  ```
114
123
 
115
124
  **[See more examples →](https://nexus-aissam.github.io/bun-image-turbo/examples/)**
@@ -148,6 +157,16 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
148
157
 
149
158
  > **sharp does NOT support HEIC/HEIF files!** bun-image-turbo is the only high-performance library with native HEIC support.
150
159
 
160
+ ### EXIF Metadata (NEW in v1.3.0)
161
+
162
+ | Operation | JPEG | WebP | Notes |
163
+ |-----------|-----:|-----:|:------|
164
+ | writeExif (simple) | **0.24ms** | **0.10ms** | 3 fields |
165
+ | writeExif (full) | **0.20ms** | **0.05ms** | 10 fields + JSON |
166
+ | stripExif | **0.26ms** | **0.08ms** | Remove all metadata |
167
+
168
+ > **Perfect for AI-generated images!** Store prompts, seeds, and generation parameters in standard EXIF fields.
169
+
151
170
  **[Full benchmark details →](https://nexus-aissam.github.io/bun-image-turbo/guide/performance)**
152
171
 
153
172
  ---
@@ -172,6 +191,7 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
172
191
  - **Shrink-on-Decode** - Decode JPEG/HEIC at reduced resolution for faster thumbnails
173
192
  - **Adaptive Algorithms** - Auto-selects optimal resize filter based on scale factor
174
193
  - **Native HEIC Support** - The only high-performance library with HEIC/HEIF decoding
194
+ - **EXIF Metadata Writing** - Write/strip EXIF data for AI image attribution
175
195
  - **Blurhash Generation** - Built-in compact placeholder generation
176
196
  - **Multi-Step Resize** - Progressive halving for large scale reductions
177
197
  - **Async & Sync APIs** - Both async and sync versions available
@@ -191,8 +211,10 @@ Tested on Apple M1 Pro with Bun 1.3.3 (compared to sharp v0.34.5):
191
211
  | `toPng()` | Convert to PNG | [→](https://nexus-aissam.github.io/bun-image-turbo/api/to-png) |
192
212
  | `toWebp()` | Convert to WebP | [→](https://nexus-aissam.github.io/bun-image-turbo/api/to-webp) |
193
213
  | `blurhash()` | Generate placeholder hash | [→](https://nexus-aissam.github.io/bun-image-turbo/api/blurhash) |
214
+ | `writeExif()` | Write EXIF metadata | [→](https://nexus-aissam.github.io/bun-image-turbo/api/exif) |
215
+ | `stripExif()` | Remove EXIF metadata | [→](https://nexus-aissam.github.io/bun-image-turbo/api/exif) |
194
216
 
195
- All functions have sync variants (`metadataSync`, `resizeSync`, etc.)
217
+ All functions have sync variants (`metadataSync`, `resizeSync`, `writeExifSync`, etc.)
196
218
 
197
219
  **[Full API Reference →](https://nexus-aissam.github.io/bun-image-turbo/api/)**
198
220
 
@@ -253,6 +275,29 @@ bun run batch # Parallel batch processing
253
275
  | [api-endpoint.ts](./examples/api-endpoint.ts) | HTTP image processing server |
254
276
  | [batch-processing.ts](./examples/batch-processing.ts) | Parallel multi-file processing |
255
277
 
278
+ ### EXIF Metadata Example
279
+
280
+ ```typescript
281
+ import { writeExif, toWebp, stripExif } from 'bun-image-turbo';
282
+
283
+ // Add AI generation metadata to WebP
284
+ const webp = await toWebp(imageBuffer, { quality: 90 });
285
+ const withMetadata = await writeExif(webp, {
286
+ imageDescription: 'A sunset over mountains',
287
+ artist: 'Stable Diffusion XL',
288
+ software: 'ComfyUI',
289
+ userComment: JSON.stringify({
290
+ prompt: 'sunset over mountains, 8k',
291
+ seed: 12345,
292
+ steps: 30,
293
+ cfg_scale: 7.5
294
+ })
295
+ });
296
+
297
+ // Strip metadata for privacy
298
+ const clean = await stripExif(photoWithGPS);
299
+ ```
300
+
256
301
  **[All examples →](https://nexus-aissam.github.io/bun-image-turbo/examples/)**
257
302
 
258
303
  ---
@@ -346,6 +391,7 @@ MIT License - see [LICENSE](LICENSE) for details.
346
391
  - [fast_image_resize](https://crates.io/crates/fast_image_resize) - Fast image resizing with Rayon
347
392
  - [webp](https://crates.io/crates/webp) - WebP encoding/decoding
348
393
  - [libheif-rs](https://crates.io/crates/libheif-rs) - HEIC/HEIF decoding via libheif
394
+ - [img-parts](https://crates.io/crates/img-parts) - EXIF/XMP metadata manipulation
349
395
  - [blurhash](https://crates.io/crates/blurhash) - Blurhash generation
350
396
  - [napi-rs](https://napi.rs/) - Rust bindings for Node.js
351
397
 
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,6 +152,8 @@ 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
  /**
@@ -302,6 +327,52 @@ declare function transformSync(input: Buffer, options: TransformOptions): Buffer
302
327
  * Generate blurhash from image synchronously
303
328
  */
304
329
  declare function blurhashSync(input: Buffer, componentsX?: number, componentsY?: number): BlurHashResult;
330
+ /**
331
+ * Write EXIF metadata to an image asynchronously
332
+ *
333
+ * Supports JPEG and WebP formats.
334
+ *
335
+ * @param input - Image buffer
336
+ * @param options - EXIF metadata options
337
+ * @returns Promise resolving to image buffer with EXIF data
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * const withExif = await writeExif(imageBuffer, {
342
+ * imageDescription: 'A beautiful sunset over the ocean',
343
+ * artist: 'John Doe',
344
+ * software: 'My AI App v1.0',
345
+ * userComment: JSON.stringify({ model: 'stable-diffusion', seed: 12345 })
346
+ * });
347
+ * ```
348
+ */
349
+ declare function writeExif(input: Buffer, options: ExifOptions): Promise<Buffer>;
350
+ /**
351
+ * Write EXIF metadata to an image synchronously
352
+ *
353
+ * Supports JPEG and WebP formats.
354
+ */
355
+ declare function writeExifSync(input: Buffer, options: ExifOptions): Buffer;
356
+ /**
357
+ * Strip all EXIF metadata from an image asynchronously
358
+ *
359
+ * Supports JPEG and WebP formats.
360
+ *
361
+ * @param input - Image buffer
362
+ * @returns Promise resolving to image buffer without EXIF data
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const stripped = await stripExif(imageBuffer);
367
+ * ```
368
+ */
369
+ declare function stripExif(input: Buffer): Promise<Buffer>;
370
+ /**
371
+ * Strip all EXIF metadata from an image synchronously
372
+ *
373
+ * Supports JPEG and WebP formats.
374
+ */
375
+ declare function stripExifSync(input: Buffer): Buffer;
305
376
  /**
306
377
  * Get library version
307
378
  */
@@ -321,7 +392,11 @@ declare const _default: {
321
392
  transformSync: typeof transformSync;
322
393
  blurhash: typeof blurhash;
323
394
  blurhashSync: typeof blurhashSync;
395
+ writeExif: typeof writeExif;
396
+ writeExifSync: typeof writeExifSync;
397
+ stripExif: typeof stripExif;
398
+ stripExifSync: typeof stripExifSync;
324
399
  version: typeof version;
325
400
  };
326
401
 
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 };
402
+ 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,6 +152,8 @@ 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
  /**
@@ -302,6 +327,52 @@ declare function transformSync(input: Buffer, options: TransformOptions): Buffer
302
327
  * Generate blurhash from image synchronously
303
328
  */
304
329
  declare function blurhashSync(input: Buffer, componentsX?: number, componentsY?: number): BlurHashResult;
330
+ /**
331
+ * Write EXIF metadata to an image asynchronously
332
+ *
333
+ * Supports JPEG and WebP formats.
334
+ *
335
+ * @param input - Image buffer
336
+ * @param options - EXIF metadata options
337
+ * @returns Promise resolving to image buffer with EXIF data
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * const withExif = await writeExif(imageBuffer, {
342
+ * imageDescription: 'A beautiful sunset over the ocean',
343
+ * artist: 'John Doe',
344
+ * software: 'My AI App v1.0',
345
+ * userComment: JSON.stringify({ model: 'stable-diffusion', seed: 12345 })
346
+ * });
347
+ * ```
348
+ */
349
+ declare function writeExif(input: Buffer, options: ExifOptions): Promise<Buffer>;
350
+ /**
351
+ * Write EXIF metadata to an image synchronously
352
+ *
353
+ * Supports JPEG and WebP formats.
354
+ */
355
+ declare function writeExifSync(input: Buffer, options: ExifOptions): Buffer;
356
+ /**
357
+ * Strip all EXIF metadata from an image asynchronously
358
+ *
359
+ * Supports JPEG and WebP formats.
360
+ *
361
+ * @param input - Image buffer
362
+ * @returns Promise resolving to image buffer without EXIF data
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const stripped = await stripExif(imageBuffer);
367
+ * ```
368
+ */
369
+ declare function stripExif(input: Buffer): Promise<Buffer>;
370
+ /**
371
+ * Strip all EXIF metadata from an image synchronously
372
+ *
373
+ * Supports JPEG and WebP formats.
374
+ */
375
+ declare function stripExifSync(input: Buffer): Buffer;
305
376
  /**
306
377
  * Get library version
307
378
  */
@@ -321,7 +392,11 @@ declare const _default: {
321
392
  transformSync: typeof transformSync;
322
393
  blurhash: typeof blurhash;
323
394
  blurhashSync: typeof blurhashSync;
395
+ writeExif: typeof writeExif;
396
+ writeExifSync: typeof writeExifSync;
397
+ stripExif: typeof stripExif;
398
+ stripExifSync: typeof stripExifSync;
324
399
  version: typeof version;
325
400
  };
326
401
 
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 };
402
+ 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,7 +37,9 @@ __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);
41
45
  var import_fs = require("fs");
@@ -154,6 +158,9 @@ function toNapiTransformOptions(options) {
154
158
  result.sharpen = options.sharpen;
155
159
  result.brightness = options.brightness;
156
160
  result.contrast = options.contrast;
161
+ if (options.exif) {
162
+ result.exif = options.exif;
163
+ }
157
164
  return result;
158
165
  }
159
166
  async function metadata(input) {
@@ -198,6 +205,18 @@ function transformSync(input, options) {
198
205
  function blurhashSync(input, componentsX, componentsY) {
199
206
  return native.blurhashSync(input, componentsX, componentsY);
200
207
  }
208
+ async function writeExif(input, options) {
209
+ return native.writeExif(input, options);
210
+ }
211
+ function writeExifSync(input, options) {
212
+ return native.writeExifSync(input, options);
213
+ }
214
+ async function stripExif(input) {
215
+ return native.stripExif(input);
216
+ }
217
+ function stripExifSync(input) {
218
+ return native.stripExifSync(input);
219
+ }
201
220
  function version() {
202
221
  return native.version();
203
222
  }
@@ -216,6 +235,10 @@ var index_default = {
216
235
  transformSync,
217
236
  blurhash,
218
237
  blurhashSync,
238
+ writeExif,
239
+ writeExifSync,
240
+ stripExif,
241
+ stripExifSync,
219
242
  version
220
243
  };
221
244
  // Annotate the CommonJS export names for ESM import in node:
@@ -226,6 +249,8 @@ var index_default = {
226
249
  metadataSync,
227
250
  resize,
228
251
  resizeSync,
252
+ stripExif,
253
+ stripExifSync,
229
254
  toJpeg,
230
255
  toJpegSync,
231
256
  toPng,
@@ -234,5 +259,7 @@ var index_default = {
234
259
  toWebpSync,
235
260
  transform,
236
261
  transformSync,
237
- version
262
+ version,
263
+ writeExif,
264
+ writeExifSync
238
265
  });
package/dist/index.mjs CHANGED
@@ -121,6 +121,9 @@ function toNapiTransformOptions(options) {
121
121
  result.sharpen = options.sharpen;
122
122
  result.brightness = options.brightness;
123
123
  result.contrast = options.contrast;
124
+ if (options.exif) {
125
+ result.exif = options.exif;
126
+ }
124
127
  return result;
125
128
  }
126
129
  async function metadata(input) {
@@ -165,6 +168,18 @@ function transformSync(input, options) {
165
168
  function blurhashSync(input, componentsX, componentsY) {
166
169
  return native.blurhashSync(input, componentsX, componentsY);
167
170
  }
171
+ async function writeExif(input, options) {
172
+ return native.writeExif(input, options);
173
+ }
174
+ function writeExifSync(input, options) {
175
+ return native.writeExifSync(input, options);
176
+ }
177
+ async function stripExif(input) {
178
+ return native.stripExif(input);
179
+ }
180
+ function stripExifSync(input) {
181
+ return native.stripExifSync(input);
182
+ }
168
183
  function version() {
169
184
  return native.version();
170
185
  }
@@ -183,6 +198,10 @@ var index_default = {
183
198
  transformSync,
184
199
  blurhash,
185
200
  blurhashSync,
201
+ writeExif,
202
+ writeExifSync,
203
+ stripExif,
204
+ stripExifSync,
186
205
  version
187
206
  };
188
207
  export {
@@ -193,6 +212,8 @@ export {
193
212
  metadataSync,
194
213
  resize,
195
214
  resizeSync,
215
+ stripExif,
216
+ stripExifSync,
196
217
  toJpeg,
197
218
  toJpegSync,
198
219
  toPng,
@@ -201,5 +222,7 @@ export {
201
222
  toWebpSync,
202
223
  transform,
203
224
  transformSync,
204
- version
225
+ version,
226
+ writeExif,
227
+ writeExifSync
205
228
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-image-turbo",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "author": "Aissam Irhir <aissamirhir@gmail.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -92,12 +92,12 @@
92
92
  },
93
93
  "types": "./dist/index.d.ts",
94
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"
95
+ "bun-image-turbo-darwin-arm64": "1.3.0",
96
+ "bun-image-turbo-darwin-x64": "1.3.0",
97
+ "bun-image-turbo-linux-arm64-gnu": "1.3.0",
98
+ "bun-image-turbo-linux-x64-gnu": "1.3.0",
99
+ "bun-image-turbo-linux-x64-musl": "1.3.0",
100
+ "bun-image-turbo-win32-arm64-msvc": "1.3.0",
101
+ "bun-image-turbo-win32-x64-msvc": "1.3.0"
102
102
  }
103
103
  }