@blinkdotnew/sdk 0.14.12 → 0.16.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
@@ -201,13 +201,13 @@ const unsubscribe = blink.auth.onAuthStateChanged((state) => {
201
201
 
202
202
  **🎉 NEW: Automatic Case Conversion!**
203
203
  The SDK now automatically converts between JavaScript camelCase and SQL snake_case:
204
- - **Write code in camelCase**: `userId`, `createdAt`, `isCompleted`
205
- - **Stored as snake_case**: `user_id`, `created_at`, `is_completed`
204
+ - **Table names**: `blink.db.emailDrafts` `email_drafts` table
205
+ - **Field names**: `userId`, `createdAt`, `isCompleted` → `user_id`, `created_at`, `is_completed`
206
206
  - **No manual conversion needed!**
207
207
 
208
208
  **⚠️ Important: Always Use camelCase in Your Code**
209
- - ✅ **Correct**: `{ userId: user.id, createdAt: new Date() }`
210
- - ❌ **Wrong**: `{ user_id: user.id, created_at: new Date() }`
209
+ - ✅ **Correct**: `blink.db.emailDrafts.create({ userId: user.id, createdAt: new Date() })`
210
+ - ❌ **Wrong**: `blink.db.email_drafts.create({ user_id: user.id, created_at: new Date() })`
211
211
 
212
212
 
213
213
  ```typescript
@@ -353,12 +353,27 @@ const { object: todoList } = await blink.ai.generateObject({
353
353
  // })
354
354
  // Error: "schema must be a JSON Schema of 'type: \"object\"', got 'type: \"array\"'"
355
355
 
356
- // Image generation
356
+ // Generate and modify images with AI
357
+
358
+ // Basic image generation - returns public URLs directly
357
359
  const { data } = await blink.ai.generateImage({
358
- prompt: 'A serene landscape',
360
+ prompt: 'A serene landscape with mountains and a lake at sunset',
359
361
  size: '1024x1024',
360
- quality: 'hd'
362
+ quality: 'hd',
363
+ n: 2
364
+ })
365
+ console.log('Image URL:', data[0].url)
366
+
367
+ // Image editing - transform existing images with prompts
368
+ const { data: headshots } = await blink.ai.modifyImage({
369
+ images: ['https://storage.example.com/user-photo.jpg'], // ... up to 16 images maximum!
370
+ prompt: 'Generate professional business headshot with studio lighting for this person.',
371
+ quality: 'hd',
372
+ n: 4
361
373
  })
374
+ // Options: size ('1024x1024', '1536x1024'), quality ('standard'|'hd'),
375
+ // background ('auto'|'transparent'|'opaque'), n (1-10 images)
376
+
362
377
 
363
378
  // Speech synthesis
364
379
  const { url } = await blink.ai.generateSpeech({
package/dist/index.d.mts CHANGED
@@ -119,8 +119,17 @@ interface FileObject {
119
119
  }
120
120
  interface BlinkStorage {
121
121
  upload(file: File | Blob | Buffer, path: string, options?: StorageUploadOptions): Promise<StorageUploadResponse>;
122
+ download(path: string, options?: {
123
+ filename?: string;
124
+ }): Promise<StorageDownloadResponse>;
122
125
  remove(...paths: string[]): Promise<void>;
123
126
  }
127
+ interface StorageDownloadResponse {
128
+ downloadUrl: string;
129
+ filename: string;
130
+ contentType?: string;
131
+ size?: number;
132
+ }
124
133
  interface TokenUsage {
125
134
  promptTokens: number;
126
135
  completionTokens: number;
@@ -211,10 +220,15 @@ interface ObjectGenerationResponse {
211
220
  interface ImageGenerationRequest {
212
221
  model?: string;
213
222
  prompt: string;
223
+ images?: string[];
214
224
  size?: string;
215
225
  quality?: 'standard' | 'hd';
226
+ background?: 'auto' | 'transparent' | 'opaque';
216
227
  n?: number;
217
228
  response_format?: 'url' | 'b64_json';
229
+ output_format?: 'png' | 'jpeg' | 'webp';
230
+ output_compression?: number;
231
+ moderation?: 'auto' | 'low';
218
232
  signal?: AbortSignal;
219
233
  }
220
234
  interface ImageGenerationResponse {
@@ -273,6 +287,15 @@ interface BlinkAI {
273
287
  generateObject(options: ObjectGenerationRequest): Promise<ObjectGenerationResponse>;
274
288
  streamObject(options: ObjectGenerationRequest, onPartial: (partial: any) => void): Promise<ObjectGenerationResponse>;
275
289
  generateImage(options: ImageGenerationRequest): Promise<ImageGenerationResponse>;
290
+ modifyImage(options: {
291
+ images: string[];
292
+ prompt: string;
293
+ size?: string;
294
+ quality?: "standard" | "hd";
295
+ n?: number;
296
+ background?: "auto" | "transparent" | "opaque";
297
+ signal?: AbortSignal;
298
+ }): Promise<ImageGenerationResponse>;
276
299
  generateSpeech(options: SpeechGenerationRequest): Promise<SpeechGenerationResponse>;
277
300
  transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>;
278
301
  }
@@ -660,10 +683,15 @@ declare class HttpClient {
660
683
  } | undefined, onPartial: (partial: any) => void): Promise<any>;
661
684
  aiImage(prompt: string, options?: {
662
685
  model?: string;
686
+ images?: string[];
663
687
  size?: string;
664
688
  quality?: 'standard' | 'hd';
689
+ background?: 'auto' | 'transparent' | 'opaque';
665
690
  n?: number;
666
691
  response_format?: 'url' | 'b64_json';
692
+ output_format?: 'png' | 'jpeg' | 'webp';
693
+ output_compression?: number;
694
+ moderation?: 'auto' | 'low';
667
695
  signal?: AbortSignal;
668
696
  }): Promise<BlinkResponse<any>>;
669
697
  aiSpeech(text: string, options?: {
@@ -834,6 +862,7 @@ declare class BlinkAuth {
834
862
  declare class BlinkTable<T = any> implements TableOperations<T> {
835
863
  private tableName;
836
864
  private httpClient;
865
+ private readonly actualTableName;
837
866
  constructor(tableName: string, httpClient: HttpClient);
838
867
  /**
839
868
  * Create a single record
@@ -1129,6 +1158,32 @@ declare class BlinkStorageImpl implements BlinkStorage {
1129
1158
  * ```
1130
1159
  */
1131
1160
  upload(file: File | Blob | Buffer, path: string, options?: StorageUploadOptions): Promise<StorageUploadResponse>;
1161
+ /**
1162
+ * Get a download URL for a file that triggers browser download
1163
+ *
1164
+ * @param path - Path to the file in project storage
1165
+ * @param options - Download options including custom filename
1166
+ * @returns Promise resolving to download response with download URL
1167
+ *
1168
+ * @example
1169
+ * ```ts
1170
+ * // Download with original filename
1171
+ * const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg');
1172
+ * window.open(downloadUrl, '_blank');
1173
+ *
1174
+ * // Download with custom filename
1175
+ * const { downloadUrl } = await blink.storage.download(
1176
+ * 'images/photo.jpg',
1177
+ * { filename: 'my-photo.jpg' }
1178
+ * );
1179
+ *
1180
+ * // Create download link in React
1181
+ * <a href={downloadUrl} download={filename}>Download Image</a>
1182
+ * ```
1183
+ */
1184
+ download(path: string, options?: {
1185
+ filename?: string;
1186
+ }): Promise<StorageDownloadResponse>;
1132
1187
  /**
1133
1188
  * Remove one or more files from project storage
1134
1189
  *
@@ -1361,15 +1416,15 @@ declare class BlinkAIImpl implements BlinkAI {
1361
1416
  */
1362
1417
  streamObject(options: ObjectGenerationRequest, onPartial: (partial: any) => void): Promise<ObjectGenerationResponse>;
1363
1418
  /**
1364
- * Generates images from text descriptions using AI image models.
1419
+ * Generates images from text descriptions using AI.
1365
1420
  *
1366
1421
  * @param options - Object containing:
1367
- * - `prompt`: Text description of the image to generate (required)
1368
- * - `size`: Image dimensions (e.g., "1024x1024", "512x512") - varies by model
1369
- * - `quality`: Image quality ("standard" or "hd")
1422
+ * - `prompt`: Text description of the desired image (required)
1423
+ * - `size`: Image dimensions (default: "1024x1024")
1424
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
1370
1425
  * - `n`: Number of images to generate (default: 1)
1371
- * - `response_format`: Output format ("url" or "b64_json")
1372
- * - Plus optional model, signal parameters
1426
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
1427
+ * - Plus optional signal parameter
1373
1428
  *
1374
1429
  * @example
1375
1430
  * ```ts
@@ -1382,31 +1437,103 @@ declare class BlinkAIImpl implements BlinkAI {
1382
1437
  * // High-quality image with specific size
1383
1438
  * const { data } = await blink.ai.generateImage({
1384
1439
  * prompt: "A futuristic city skyline with flying cars",
1385
- * size: "1792x1024",
1440
+ * size: "1536x1024",
1386
1441
  * quality: "hd",
1387
- * model: "dall-e-3"
1442
+ * background: "transparent"
1388
1443
  * });
1389
1444
  *
1390
1445
  * // Multiple images
1391
1446
  * const { data } = await blink.ai.generateImage({
1392
1447
  * prompt: "A cute robot mascot for a tech company",
1393
1448
  * n: 3,
1394
- * size: "1024x1024"
1449
+ * size: "1024x1024",
1450
+ * quality: "hd"
1395
1451
  * });
1396
1452
  * data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url));
1453
+ * ```
1397
1454
  *
1398
- * // Base64 format for direct embedding
1399
- * const { data } = await blink.ai.generateImage({
1400
- * prompt: "A minimalist logo design",
1401
- * response_format: "b64_json"
1455
+ * @returns Promise<ImageGenerationResponse> - Object containing:
1456
+ * - `data`: Array of generated images with URLs
1457
+ * - `created`: Timestamp of generation
1458
+ * - `usage`: Token usage information
1459
+ */
1460
+ generateImage(options: {
1461
+ prompt: string;
1462
+ size?: string;
1463
+ quality?: "standard" | "hd";
1464
+ n?: number;
1465
+ background?: "auto" | "transparent" | "opaque";
1466
+ signal?: AbortSignal;
1467
+ }): Promise<ImageGenerationResponse>;
1468
+ /**
1469
+ * Modifies existing images using AI with text prompts for image-to-image editing.
1470
+ *
1471
+ * @param options - Object containing:
1472
+ * - `images`: Array of public image URLs to modify (required, up to 16 images)
1473
+ * - `prompt`: Text description of desired modifications (required)
1474
+ * - `size`: Output image dimensions (default: "auto")
1475
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
1476
+ * - `n`: Number of output images to generate (default: 1)
1477
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
1478
+ * - Plus optional signal parameter
1479
+ *
1480
+ * @example
1481
+ * ```ts
1482
+ * // Professional headshots from casual photos
1483
+ * const { data } = await blink.ai.modifyImage({
1484
+ * images: [
1485
+ * "https://storage.example.com/user-photo-1.jpg",
1486
+ * "https://storage.example.com/user-photo-2.jpg"
1487
+ * ],
1488
+ * prompt: "Transform into professional business headshots with studio lighting",
1489
+ * quality: "hd",
1490
+ * n: 4
1491
+ * });
1492
+ * data.forEach((img, i) => console.log(`Headshot ${i+1}:`, img.url));
1493
+ *
1494
+ * // Artistic style transformation
1495
+ * const { data } = await blink.ai.modifyImage({
1496
+ * images: ["https://storage.example.com/portrait.jpg"],
1497
+ * prompt: "Transform into oil painting style with dramatic lighting",
1498
+ * quality: "hd",
1499
+ * size: "1024x1024"
1500
+ * });
1501
+ *
1502
+ * // Background replacement
1503
+ * const { data } = await blink.ai.modifyImage({
1504
+ * images: ["https://storage.example.com/product.jpg"],
1505
+ * prompt: "Remove background and place on clean white studio background",
1506
+ * background: "transparent",
1507
+ * n: 2
1508
+ * });
1509
+ *
1510
+ * // Batch processing multiple photos
1511
+ * const userPhotos = [
1512
+ * "https://storage.example.com/photo1.jpg",
1513
+ * "https://storage.example.com/photo2.jpg",
1514
+ * "https://storage.example.com/photo3.jpg"
1515
+ * ];
1516
+ * const { data } = await blink.ai.modifyImage({
1517
+ * images: userPhotos,
1518
+ * prompt: "Convert to black and white vintage style photographs",
1519
+ * quality: "hd"
1402
1520
  * });
1403
- * console.log("Base64 data:", data[0].b64_json);
1404
1521
  * ```
1405
1522
  *
1406
1523
  * @returns Promise<ImageGenerationResponse> - Object containing:
1407
- * - `data`: Array of generated images with url or b64_json
1524
+ * - `data`: Array of modified images with URLs
1525
+ * - `created`: Timestamp of generation
1526
+ * - `usage`: Token usage information
1408
1527
  */
1409
- generateImage(options: ImageGenerationRequest): Promise<ImageGenerationResponse>;
1528
+ modifyImage(options: {
1529
+ images: string[];
1530
+ prompt: string;
1531
+ size?: string;
1532
+ quality?: "standard" | "hd";
1533
+ n?: number;
1534
+ background?: "auto" | "transparent" | "opaque";
1535
+ signal?: AbortSignal;
1536
+ }): Promise<ImageGenerationResponse>;
1410
1537
  /**
1411
1538
  * Converts text to speech using AI voice synthesis models.
1412
1539
  *
package/dist/index.d.ts CHANGED
@@ -119,8 +119,17 @@ interface FileObject {
119
119
  }
120
120
  interface BlinkStorage {
121
121
  upload(file: File | Blob | Buffer, path: string, options?: StorageUploadOptions): Promise<StorageUploadResponse>;
122
+ download(path: string, options?: {
123
+ filename?: string;
124
+ }): Promise<StorageDownloadResponse>;
122
125
  remove(...paths: string[]): Promise<void>;
123
126
  }
127
+ interface StorageDownloadResponse {
128
+ downloadUrl: string;
129
+ filename: string;
130
+ contentType?: string;
131
+ size?: number;
132
+ }
124
133
  interface TokenUsage {
125
134
  promptTokens: number;
126
135
  completionTokens: number;
@@ -211,10 +220,15 @@ interface ObjectGenerationResponse {
211
220
  interface ImageGenerationRequest {
212
221
  model?: string;
213
222
  prompt: string;
223
+ images?: string[];
214
224
  size?: string;
215
225
  quality?: 'standard' | 'hd';
226
+ background?: 'auto' | 'transparent' | 'opaque';
216
227
  n?: number;
217
228
  response_format?: 'url' | 'b64_json';
229
+ output_format?: 'png' | 'jpeg' | 'webp';
230
+ output_compression?: number;
231
+ moderation?: 'auto' | 'low';
218
232
  signal?: AbortSignal;
219
233
  }
220
234
  interface ImageGenerationResponse {
@@ -273,6 +287,15 @@ interface BlinkAI {
273
287
  generateObject(options: ObjectGenerationRequest): Promise<ObjectGenerationResponse>;
274
288
  streamObject(options: ObjectGenerationRequest, onPartial: (partial: any) => void): Promise<ObjectGenerationResponse>;
275
289
  generateImage(options: ImageGenerationRequest): Promise<ImageGenerationResponse>;
290
+ modifyImage(options: {
291
+ images: string[];
292
+ prompt: string;
293
+ size?: string;
294
+ quality?: "standard" | "hd";
295
+ n?: number;
296
+ background?: "auto" | "transparent" | "opaque";
297
+ signal?: AbortSignal;
298
+ }): Promise<ImageGenerationResponse>;
276
299
  generateSpeech(options: SpeechGenerationRequest): Promise<SpeechGenerationResponse>;
277
300
  transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>;
278
301
  }
@@ -660,10 +683,15 @@ declare class HttpClient {
660
683
  } | undefined, onPartial: (partial: any) => void): Promise<any>;
661
684
  aiImage(prompt: string, options?: {
662
685
  model?: string;
686
+ images?: string[];
663
687
  size?: string;
664
688
  quality?: 'standard' | 'hd';
689
+ background?: 'auto' | 'transparent' | 'opaque';
665
690
  n?: number;
666
691
  response_format?: 'url' | 'b64_json';
692
+ output_format?: 'png' | 'jpeg' | 'webp';
693
+ output_compression?: number;
694
+ moderation?: 'auto' | 'low';
667
695
  signal?: AbortSignal;
668
696
  }): Promise<BlinkResponse<any>>;
669
697
  aiSpeech(text: string, options?: {
@@ -834,6 +862,7 @@ declare class BlinkAuth {
834
862
  declare class BlinkTable<T = any> implements TableOperations<T> {
835
863
  private tableName;
836
864
  private httpClient;
865
+ private readonly actualTableName;
837
866
  constructor(tableName: string, httpClient: HttpClient);
838
867
  /**
839
868
  * Create a single record
@@ -1129,6 +1158,32 @@ declare class BlinkStorageImpl implements BlinkStorage {
1129
1158
  * ```
1130
1159
  */
1131
1160
  upload(file: File | Blob | Buffer, path: string, options?: StorageUploadOptions): Promise<StorageUploadResponse>;
1161
+ /**
1162
+ * Get a download URL for a file that triggers browser download
1163
+ *
1164
+ * @param path - Path to the file in project storage
1165
+ * @param options - Download options including custom filename
1166
+ * @returns Promise resolving to download response with download URL
1167
+ *
1168
+ * @example
1169
+ * ```ts
1170
+ * // Download with original filename
1171
+ * const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg');
1172
+ * window.open(downloadUrl, '_blank');
1173
+ *
1174
+ * // Download with custom filename
1175
+ * const { downloadUrl } = await blink.storage.download(
1176
+ * 'images/photo.jpg',
1177
+ * { filename: 'my-photo.jpg' }
1178
+ * );
1179
+ *
1180
+ * // Create download link in React
1181
+ * <a href={downloadUrl} download={filename}>Download Image</a>
1182
+ * ```
1183
+ */
1184
+ download(path: string, options?: {
1185
+ filename?: string;
1186
+ }): Promise<StorageDownloadResponse>;
1132
1187
  /**
1133
1188
  * Remove one or more files from project storage
1134
1189
  *
@@ -1361,15 +1416,15 @@ declare class BlinkAIImpl implements BlinkAI {
1361
1416
  */
1362
1417
  streamObject(options: ObjectGenerationRequest, onPartial: (partial: any) => void): Promise<ObjectGenerationResponse>;
1363
1418
  /**
1364
- * Generates images from text descriptions using AI image models.
1419
+ * Generates images from text descriptions using AI.
1365
1420
  *
1366
1421
  * @param options - Object containing:
1367
- * - `prompt`: Text description of the image to generate (required)
1368
- * - `size`: Image dimensions (e.g., "1024x1024", "512x512") - varies by model
1369
- * - `quality`: Image quality ("standard" or "hd")
1422
+ * - `prompt`: Text description of the desired image (required)
1423
+ * - `size`: Image dimensions (default: "1024x1024")
1424
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
1370
1425
  * - `n`: Number of images to generate (default: 1)
1371
- * - `response_format`: Output format ("url" or "b64_json")
1372
- * - Plus optional model, signal parameters
1426
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
1427
+ * - Plus optional signal parameter
1373
1428
  *
1374
1429
  * @example
1375
1430
  * ```ts
@@ -1382,31 +1437,103 @@ declare class BlinkAIImpl implements BlinkAI {
1382
1437
  * // High-quality image with specific size
1383
1438
  * const { data } = await blink.ai.generateImage({
1384
1439
  * prompt: "A futuristic city skyline with flying cars",
1385
- * size: "1792x1024",
1440
+ * size: "1536x1024",
1386
1441
  * quality: "hd",
1387
- * model: "dall-e-3"
1442
+ * background: "transparent"
1388
1443
  * });
1389
1444
  *
1390
1445
  * // Multiple images
1391
1446
  * const { data } = await blink.ai.generateImage({
1392
1447
  * prompt: "A cute robot mascot for a tech company",
1393
1448
  * n: 3,
1394
- * size: "1024x1024"
1449
+ * size: "1024x1024",
1450
+ * quality: "hd"
1395
1451
  * });
1396
1452
  * data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url));
1453
+ * ```
1397
1454
  *
1398
- * // Base64 format for direct embedding
1399
- * const { data } = await blink.ai.generateImage({
1400
- * prompt: "A minimalist logo design",
1401
- * response_format: "b64_json"
1455
+ * @returns Promise<ImageGenerationResponse> - Object containing:
1456
+ * - `data`: Array of generated images with URLs
1457
+ * - `created`: Timestamp of generation
1458
+ * - `usage`: Token usage information
1459
+ */
1460
+ generateImage(options: {
1461
+ prompt: string;
1462
+ size?: string;
1463
+ quality?: "standard" | "hd";
1464
+ n?: number;
1465
+ background?: "auto" | "transparent" | "opaque";
1466
+ signal?: AbortSignal;
1467
+ }): Promise<ImageGenerationResponse>;
1468
+ /**
1469
+ * Modifies existing images using AI with text prompts for image-to-image editing.
1470
+ *
1471
+ * @param options - Object containing:
1472
+ * - `images`: Array of public image URLs to modify (required, up to 16 images)
1473
+ * - `prompt`: Text description of desired modifications (required)
1474
+ * - `size`: Output image dimensions (default: "auto")
1475
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
1476
+ * - `n`: Number of output images to generate (default: 1)
1477
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
1478
+ * - Plus optional signal parameter
1479
+ *
1480
+ * @example
1481
+ * ```ts
1482
+ * // Professional headshots from casual photos
1483
+ * const { data } = await blink.ai.modifyImage({
1484
+ * images: [
1485
+ * "https://storage.example.com/user-photo-1.jpg",
1486
+ * "https://storage.example.com/user-photo-2.jpg"
1487
+ * ],
1488
+ * prompt: "Transform into professional business headshots with studio lighting",
1489
+ * quality: "hd",
1490
+ * n: 4
1491
+ * });
1492
+ * data.forEach((img, i) => console.log(`Headshot ${i+1}:`, img.url));
1493
+ *
1494
+ * // Artistic style transformation
1495
+ * const { data } = await blink.ai.modifyImage({
1496
+ * images: ["https://storage.example.com/portrait.jpg"],
1497
+ * prompt: "Transform into oil painting style with dramatic lighting",
1498
+ * quality: "hd",
1499
+ * size: "1024x1024"
1500
+ * });
1501
+ *
1502
+ * // Background replacement
1503
+ * const { data } = await blink.ai.modifyImage({
1504
+ * images: ["https://storage.example.com/product.jpg"],
1505
+ * prompt: "Remove background and place on clean white studio background",
1506
+ * background: "transparent",
1507
+ * n: 2
1508
+ * });
1509
+ *
1510
+ * // Batch processing multiple photos
1511
+ * const userPhotos = [
1512
+ * "https://storage.example.com/photo1.jpg",
1513
+ * "https://storage.example.com/photo2.jpg",
1514
+ * "https://storage.example.com/photo3.jpg"
1515
+ * ];
1516
+ * const { data } = await blink.ai.modifyImage({
1517
+ * images: userPhotos,
1518
+ * prompt: "Convert to black and white vintage style photographs",
1519
+ * quality: "hd"
1402
1520
  * });
1403
- * console.log("Base64 data:", data[0].b64_json);
1404
1521
  * ```
1405
1522
  *
1406
1523
  * @returns Promise<ImageGenerationResponse> - Object containing:
1407
- * - `data`: Array of generated images with url or b64_json
1524
+ * - `data`: Array of modified images with URLs
1525
+ * - `created`: Timestamp of generation
1526
+ * - `usage`: Token usage information
1408
1527
  */
1409
- generateImage(options: ImageGenerationRequest): Promise<ImageGenerationResponse>;
1528
+ modifyImage(options: {
1529
+ images: string[];
1530
+ prompt: string;
1531
+ size?: string;
1532
+ quality?: "standard" | "hd";
1533
+ n?: number;
1534
+ background?: "auto" | "transparent" | "opaque";
1535
+ signal?: AbortSignal;
1536
+ }): Promise<ImageGenerationResponse>;
1410
1537
  /**
1411
1538
  * Converts text to speech using AI voice synthesis models.
1412
1539
  *
package/dist/index.js CHANGED
@@ -1530,6 +1530,9 @@ var BlinkAuth = class {
1530
1530
  };
1531
1531
 
1532
1532
  // src/database.ts
1533
+ function camelToSnake3(str) {
1534
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
1535
+ }
1533
1536
  function generateSecureId() {
1534
1537
  if (typeof crypto !== "undefined" && crypto.getRandomValues) {
1535
1538
  const array = new Uint8Array(16);
@@ -1552,14 +1555,16 @@ var BlinkTable = class {
1552
1555
  constructor(tableName, httpClient) {
1553
1556
  this.tableName = tableName;
1554
1557
  this.httpClient = httpClient;
1558
+ this.actualTableName = camelToSnake3(tableName);
1555
1559
  }
1560
+ actualTableName;
1556
1561
  /**
1557
1562
  * Create a single record
1558
1563
  */
1559
1564
  async create(data, options = {}) {
1560
1565
  const record = ensureRecordId(data);
1561
1566
  const response = await this.httpClient.dbPost(
1562
- this.tableName,
1567
+ this.actualTableName,
1563
1568
  record,
1564
1569
  { returning: options.returning !== false }
1565
1570
  );
@@ -1575,7 +1580,7 @@ var BlinkTable = class {
1575
1580
  async createMany(data, options = {}) {
1576
1581
  const records = data.map(ensureRecordId);
1577
1582
  const response = await this.httpClient.dbPost(
1578
- this.tableName,
1583
+ this.actualTableName,
1579
1584
  records,
1580
1585
  { returning: options.returning !== false }
1581
1586
  );
@@ -1595,7 +1600,7 @@ var BlinkTable = class {
1595
1600
  }
1596
1601
  const record = ensureRecordId(data);
1597
1602
  const response = await this.httpClient.request(
1598
- `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}?on_conflict=${options.onConflict || "id"}`,
1603
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.actualTableName}?on_conflict=${options.onConflict || "id"}`,
1599
1604
  {
1600
1605
  method: "POST",
1601
1606
  body: record,
@@ -1621,7 +1626,7 @@ var BlinkTable = class {
1621
1626
  headers["Prefer"] = `${headers["Prefer"] || ""} resolution=merge-duplicates`.trim();
1622
1627
  }
1623
1628
  const response = await this.httpClient.request(
1624
- `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}?on_conflict=${options.onConflict || "id"}`,
1629
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.actualTableName}?on_conflict=${options.onConflict || "id"}`,
1625
1630
  {
1626
1631
  method: "POST",
1627
1632
  body: records,
@@ -1639,7 +1644,7 @@ var BlinkTable = class {
1639
1644
  id: `eq.${id}`,
1640
1645
  limit: "1"
1641
1646
  };
1642
- const response = await this.httpClient.dbGet(this.tableName, searchParams);
1647
+ const response = await this.httpClient.dbGet(this.actualTableName, searchParams);
1643
1648
  const records = response.data;
1644
1649
  if (records.length === 0) {
1645
1650
  return null;
@@ -1652,7 +1657,7 @@ var BlinkTable = class {
1652
1657
  async list(options = {}) {
1653
1658
  const queryParams = buildQuery(options);
1654
1659
  const searchParams = queryParams;
1655
- const response = await this.httpClient.dbGet(this.tableName, searchParams);
1660
+ const response = await this.httpClient.dbGet(this.actualTableName, searchParams);
1656
1661
  const records = response.data;
1657
1662
  return records;
1658
1663
  }
@@ -1664,7 +1669,7 @@ var BlinkTable = class {
1664
1669
  id: `eq.${id}`
1665
1670
  };
1666
1671
  const response = await this.httpClient.dbPatch(
1667
- this.tableName,
1672
+ this.actualTableName,
1668
1673
  data,
1669
1674
  searchParams,
1670
1675
  { returning: options.returning !== false }
@@ -1694,7 +1699,7 @@ var BlinkTable = class {
1694
1699
  const searchParams = {
1695
1700
  id: `eq.${id}`
1696
1701
  };
1697
- await this.httpClient.dbDelete(this.tableName, searchParams);
1702
+ await this.httpClient.dbDelete(this.actualTableName, searchParams);
1698
1703
  }
1699
1704
  /**
1700
1705
  * Delete multiple records based on filter
@@ -1702,7 +1707,7 @@ var BlinkTable = class {
1702
1707
  async deleteMany(options) {
1703
1708
  const queryParams = buildQuery({ where: options.where });
1704
1709
  const searchParams = queryParams;
1705
- await this.httpClient.dbDelete(this.tableName, searchParams);
1710
+ await this.httpClient.dbDelete(this.actualTableName, searchParams);
1706
1711
  }
1707
1712
  /**
1708
1713
  * Count records matching filter
@@ -1713,7 +1718,7 @@ var BlinkTable = class {
1713
1718
  select: ["id"]
1714
1719
  });
1715
1720
  const response = await this.httpClient.request(
1716
- `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}`,
1721
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.actualTableName}`,
1717
1722
  {
1718
1723
  method: "GET",
1719
1724
  searchParams: queryParams,
@@ -1882,6 +1887,74 @@ var BlinkStorageImpl = class {
1882
1887
  );
1883
1888
  }
1884
1889
  }
1890
+ /**
1891
+ * Get a download URL for a file that triggers browser download
1892
+ *
1893
+ * @param path - Path to the file in project storage
1894
+ * @param options - Download options including custom filename
1895
+ * @returns Promise resolving to download response with download URL
1896
+ *
1897
+ * @example
1898
+ * ```ts
1899
+ * // Download with original filename
1900
+ * const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg');
1901
+ * window.open(downloadUrl, '_blank');
1902
+ *
1903
+ * // Download with custom filename
1904
+ * const { downloadUrl } = await blink.storage.download(
1905
+ * 'images/photo.jpg',
1906
+ * { filename: 'my-photo.jpg' }
1907
+ * );
1908
+ *
1909
+ * // Create download link in React
1910
+ * <a href={downloadUrl} download={filename}>Download Image</a>
1911
+ * ```
1912
+ */
1913
+ async download(path, options = {}) {
1914
+ try {
1915
+ if (!path || typeof path !== "string" || !path.trim()) {
1916
+ throw new BlinkStorageError("Path must be a non-empty string");
1917
+ }
1918
+ const response = await this.httpClient.request(
1919
+ `/api/storage/${this.httpClient.projectId}/download`,
1920
+ {
1921
+ method: "GET",
1922
+ searchParams: {
1923
+ path: path.trim(),
1924
+ ...options.filename && { filename: options.filename }
1925
+ }
1926
+ }
1927
+ );
1928
+ if (response.data?.downloadUrl) {
1929
+ return {
1930
+ downloadUrl: response.data.downloadUrl,
1931
+ filename: response.data.filename || options.filename || path.split("/").pop() || "download",
1932
+ contentType: response.data.contentType,
1933
+ size: response.data.size
1934
+ };
1935
+ } else {
1936
+ throw new BlinkStorageError("Invalid response format: missing downloadUrl");
1937
+ }
1938
+ } catch (error) {
1939
+ if (error instanceof BlinkStorageError) {
1940
+ throw error;
1941
+ }
1942
+ if (error instanceof Error && "status" in error) {
1943
+ const status = error.status;
1944
+ if (status === 404) {
1945
+ throw new BlinkStorageError("File not found", 404);
1946
+ }
1947
+ if (status === 400) {
1948
+ throw new BlinkStorageError("Invalid request parameters", 400);
1949
+ }
1950
+ }
1951
+ throw new BlinkStorageError(
1952
+ `Download failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1953
+ void 0,
1954
+ { originalError: error }
1955
+ );
1956
+ }
1957
+ }
1885
1958
  /**
1886
1959
  * Remove one or more files from project storage
1887
1960
  *
@@ -2349,15 +2422,15 @@ var BlinkAIImpl = class {
2349
2422
  }
2350
2423
  }
2351
2424
  /**
2352
- * Generates images from text descriptions using AI image models.
2425
+ * Generates images from text descriptions using AI.
2353
2426
  *
2354
2427
  * @param options - Object containing:
2355
- * - `prompt`: Text description of the image to generate (required)
2356
- * - `size`: Image dimensions (e.g., "1024x1024", "512x512") - varies by model
2357
- * - `quality`: Image quality ("standard" or "hd")
2428
+ * - `prompt`: Text description of the desired image (required)
2429
+ * - `size`: Image dimensions (default: "1024x1024")
2430
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
2358
2431
  * - `n`: Number of images to generate (default: 1)
2359
- * - `response_format`: Output format ("url" or "b64_json")
2360
- * - Plus optional model, signal parameters
2432
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
2433
+ * - Plus optional signal parameter
2361
2434
  *
2362
2435
  * @example
2363
2436
  * ```ts
@@ -2370,29 +2443,25 @@ var BlinkAIImpl = class {
2370
2443
  * // High-quality image with specific size
2371
2444
  * const { data } = await blink.ai.generateImage({
2372
2445
  * prompt: "A futuristic city skyline with flying cars",
2373
- * size: "1792x1024",
2446
+ * size: "1536x1024",
2374
2447
  * quality: "hd",
2375
- * model: "dall-e-3"
2448
+ * background: "transparent"
2376
2449
  * });
2377
2450
  *
2378
2451
  * // Multiple images
2379
2452
  * const { data } = await blink.ai.generateImage({
2380
2453
  * prompt: "A cute robot mascot for a tech company",
2381
2454
  * n: 3,
2382
- * size: "1024x1024"
2455
+ * size: "1024x1024",
2456
+ * quality: "hd"
2383
2457
  * });
2384
2458
  * data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url));
2385
- *
2386
- * // Base64 format for direct embedding
2387
- * const { data } = await blink.ai.generateImage({
2388
- * prompt: "A minimalist logo design",
2389
- * response_format: "b64_json"
2390
- * });
2391
- * console.log("Base64 data:", data[0].b64_json);
2392
2459
  * ```
2393
2460
  *
2394
2461
  * @returns Promise<ImageGenerationResponse> - Object containing:
2395
- * - `data`: Array of generated images with url or b64_json
2462
+ * - `data`: Array of generated images with URLs
2463
+ * - `created`: Timestamp of generation
2464
+ * - `usage`: Token usage information
2396
2465
  */
2397
2466
  async generateImage(options) {
2398
2467
  try {
@@ -2402,11 +2471,12 @@ var BlinkAIImpl = class {
2402
2471
  const response = await this.httpClient.aiImage(
2403
2472
  options.prompt,
2404
2473
  {
2405
- model: options.model,
2474
+ model: "gpt-image-1",
2406
2475
  size: options.size,
2407
2476
  quality: options.quality,
2408
2477
  n: options.n,
2409
- response_format: options.response_format,
2478
+ background: options.background,
2479
+ response_format: "url",
2410
2480
  signal: options.signal
2411
2481
  }
2412
2482
  );
@@ -2426,10 +2496,8 @@ var BlinkAIImpl = class {
2426
2496
  return { url: item };
2427
2497
  } else if (item.url) {
2428
2498
  return item;
2429
- } else if (item.b64_json) {
2430
- return { b64_json: item.b64_json };
2431
2499
  } else {
2432
- return { url: item };
2500
+ throw new BlinkAIError("Invalid image response format");
2433
2501
  }
2434
2502
  });
2435
2503
  return imageResponse;
@@ -2444,6 +2512,129 @@ var BlinkAIImpl = class {
2444
2512
  );
2445
2513
  }
2446
2514
  }
2515
+ /**
2516
+ * Modifies existing images using AI with text prompts for image-to-image editing.
2517
+ *
2518
+ * @param options - Object containing:
2519
+ * - `images`: Array of public image URLs to modify (required, up to 16 images)
2520
+ * - `prompt`: Text description of desired modifications (required)
2521
+ * - `size`: Output image dimensions (default: "auto")
2522
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
2523
+ * - `n`: Number of output images to generate (default: 1)
2524
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
2525
+ * - Plus optional signal parameter
2526
+ *
2527
+ * @example
2528
+ * ```ts
2529
+ * // Professional headshots from casual photos
2530
+ * const { data } = await blink.ai.modifyImage({
2531
+ * images: [
2532
+ * "https://storage.example.com/user-photo-1.jpg",
2533
+ * "https://storage.example.com/user-photo-2.jpg"
2534
+ * ],
2535
+ * prompt: "Transform into professional business headshots with studio lighting",
2536
+ * quality: "hd",
2537
+ * n: 4
2538
+ * });
2539
+ * data.forEach((img, i) => console.log(`Headshot ${i+1}:`, img.url));
2540
+ *
2541
+ * // Artistic style transformation
2542
+ * const { data } = await blink.ai.modifyImage({
2543
+ * images: ["https://storage.example.com/portrait.jpg"],
2544
+ * prompt: "Transform into oil painting style with dramatic lighting",
2545
+ * quality: "hd",
2546
+ * size: "1024x1024"
2547
+ * });
2548
+ *
2549
+ * // Background replacement
2550
+ * const { data } = await blink.ai.modifyImage({
2551
+ * images: ["https://storage.example.com/product.jpg"],
2552
+ * prompt: "Remove background and place on clean white studio background",
2553
+ * background: "transparent",
2554
+ * n: 2
2555
+ * });
2556
+ *
2557
+ * // Batch processing multiple photos
2558
+ * const userPhotos = [
2559
+ * "https://storage.example.com/photo1.jpg",
2560
+ * "https://storage.example.com/photo2.jpg",
2561
+ * "https://storage.example.com/photo3.jpg"
2562
+ * ];
2563
+ * const { data } = await blink.ai.modifyImage({
2564
+ * images: userPhotos,
2565
+ * prompt: "Convert to black and white vintage style photographs",
2566
+ * quality: "hd"
2567
+ * });
2568
+ * ```
2569
+ *
2570
+ * @returns Promise<ImageGenerationResponse> - Object containing:
2571
+ * - `data`: Array of modified images with URLs
2572
+ * - `created`: Timestamp of generation
2573
+ * - `usage`: Token usage information
2574
+ */
2575
+ async modifyImage(options) {
2576
+ try {
2577
+ if (!options.prompt) {
2578
+ throw new BlinkAIError("Prompt is required");
2579
+ }
2580
+ if (!options.images || !Array.isArray(options.images) || options.images.length === 0) {
2581
+ throw new BlinkAIError("Images array is required and must contain at least one image URL");
2582
+ }
2583
+ if (options.images.length > 16) {
2584
+ throw new BlinkAIError("Maximum 16 images allowed");
2585
+ }
2586
+ for (let i = 0; i < options.images.length; i++) {
2587
+ const validation = this.validateImageUrl(options.images[i]);
2588
+ if (!validation.isValid) {
2589
+ throw new BlinkAIError(`Image ${i + 1}: ${validation.error}`);
2590
+ }
2591
+ }
2592
+ const response = await this.httpClient.aiImage(
2593
+ options.prompt,
2594
+ // Non-null assertion since we validated above
2595
+ {
2596
+ model: "gpt-image-1",
2597
+ images: options.images,
2598
+ size: options.size,
2599
+ quality: options.quality,
2600
+ n: options.n,
2601
+ background: options.background,
2602
+ response_format: "url",
2603
+ signal: options.signal
2604
+ }
2605
+ );
2606
+ let imageResponse;
2607
+ if (response.data?.result?.data) {
2608
+ imageResponse = response.data.result;
2609
+ } else if (response.data?.data) {
2610
+ imageResponse = response.data;
2611
+ } else {
2612
+ throw new BlinkAIError("Invalid response format: missing image data");
2613
+ }
2614
+ if (!Array.isArray(imageResponse.data)) {
2615
+ throw new BlinkAIError("Invalid response format: data should be an array");
2616
+ }
2617
+ imageResponse.data = imageResponse.data.map((item) => {
2618
+ if (typeof item === "string") {
2619
+ return { url: item };
2620
+ } else if (item.url) {
2621
+ return item;
2622
+ } else {
2623
+ throw new BlinkAIError("Invalid image response format");
2624
+ }
2625
+ });
2626
+ return imageResponse;
2627
+ } catch (error) {
2628
+ if (error instanceof BlinkAIError) {
2629
+ throw error;
2630
+ }
2631
+ throw new BlinkAIError(
2632
+ `Image modification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
2633
+ void 0,
2634
+ { originalError: error }
2635
+ );
2636
+ }
2637
+ }
2447
2638
  /**
2448
2639
  * Converts text to speech using AI voice synthesis models.
2449
2640
  *
package/dist/index.mjs CHANGED
@@ -1528,6 +1528,9 @@ var BlinkAuth = class {
1528
1528
  };
1529
1529
 
1530
1530
  // src/database.ts
1531
+ function camelToSnake3(str) {
1532
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
1533
+ }
1531
1534
  function generateSecureId() {
1532
1535
  if (typeof crypto !== "undefined" && crypto.getRandomValues) {
1533
1536
  const array = new Uint8Array(16);
@@ -1550,14 +1553,16 @@ var BlinkTable = class {
1550
1553
  constructor(tableName, httpClient) {
1551
1554
  this.tableName = tableName;
1552
1555
  this.httpClient = httpClient;
1556
+ this.actualTableName = camelToSnake3(tableName);
1553
1557
  }
1558
+ actualTableName;
1554
1559
  /**
1555
1560
  * Create a single record
1556
1561
  */
1557
1562
  async create(data, options = {}) {
1558
1563
  const record = ensureRecordId(data);
1559
1564
  const response = await this.httpClient.dbPost(
1560
- this.tableName,
1565
+ this.actualTableName,
1561
1566
  record,
1562
1567
  { returning: options.returning !== false }
1563
1568
  );
@@ -1573,7 +1578,7 @@ var BlinkTable = class {
1573
1578
  async createMany(data, options = {}) {
1574
1579
  const records = data.map(ensureRecordId);
1575
1580
  const response = await this.httpClient.dbPost(
1576
- this.tableName,
1581
+ this.actualTableName,
1577
1582
  records,
1578
1583
  { returning: options.returning !== false }
1579
1584
  );
@@ -1593,7 +1598,7 @@ var BlinkTable = class {
1593
1598
  }
1594
1599
  const record = ensureRecordId(data);
1595
1600
  const response = await this.httpClient.request(
1596
- `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}?on_conflict=${options.onConflict || "id"}`,
1601
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.actualTableName}?on_conflict=${options.onConflict || "id"}`,
1597
1602
  {
1598
1603
  method: "POST",
1599
1604
  body: record,
@@ -1619,7 +1624,7 @@ var BlinkTable = class {
1619
1624
  headers["Prefer"] = `${headers["Prefer"] || ""} resolution=merge-duplicates`.trim();
1620
1625
  }
1621
1626
  const response = await this.httpClient.request(
1622
- `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}?on_conflict=${options.onConflict || "id"}`,
1627
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.actualTableName}?on_conflict=${options.onConflict || "id"}`,
1623
1628
  {
1624
1629
  method: "POST",
1625
1630
  body: records,
@@ -1637,7 +1642,7 @@ var BlinkTable = class {
1637
1642
  id: `eq.${id}`,
1638
1643
  limit: "1"
1639
1644
  };
1640
- const response = await this.httpClient.dbGet(this.tableName, searchParams);
1645
+ const response = await this.httpClient.dbGet(this.actualTableName, searchParams);
1641
1646
  const records = response.data;
1642
1647
  if (records.length === 0) {
1643
1648
  return null;
@@ -1650,7 +1655,7 @@ var BlinkTable = class {
1650
1655
  async list(options = {}) {
1651
1656
  const queryParams = buildQuery(options);
1652
1657
  const searchParams = queryParams;
1653
- const response = await this.httpClient.dbGet(this.tableName, searchParams);
1658
+ const response = await this.httpClient.dbGet(this.actualTableName, searchParams);
1654
1659
  const records = response.data;
1655
1660
  return records;
1656
1661
  }
@@ -1662,7 +1667,7 @@ var BlinkTable = class {
1662
1667
  id: `eq.${id}`
1663
1668
  };
1664
1669
  const response = await this.httpClient.dbPatch(
1665
- this.tableName,
1670
+ this.actualTableName,
1666
1671
  data,
1667
1672
  searchParams,
1668
1673
  { returning: options.returning !== false }
@@ -1692,7 +1697,7 @@ var BlinkTable = class {
1692
1697
  const searchParams = {
1693
1698
  id: `eq.${id}`
1694
1699
  };
1695
- await this.httpClient.dbDelete(this.tableName, searchParams);
1700
+ await this.httpClient.dbDelete(this.actualTableName, searchParams);
1696
1701
  }
1697
1702
  /**
1698
1703
  * Delete multiple records based on filter
@@ -1700,7 +1705,7 @@ var BlinkTable = class {
1700
1705
  async deleteMany(options) {
1701
1706
  const queryParams = buildQuery({ where: options.where });
1702
1707
  const searchParams = queryParams;
1703
- await this.httpClient.dbDelete(this.tableName, searchParams);
1708
+ await this.httpClient.dbDelete(this.actualTableName, searchParams);
1704
1709
  }
1705
1710
  /**
1706
1711
  * Count records matching filter
@@ -1711,7 +1716,7 @@ var BlinkTable = class {
1711
1716
  select: ["id"]
1712
1717
  });
1713
1718
  const response = await this.httpClient.request(
1714
- `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}`,
1719
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.actualTableName}`,
1715
1720
  {
1716
1721
  method: "GET",
1717
1722
  searchParams: queryParams,
@@ -1880,6 +1885,74 @@ var BlinkStorageImpl = class {
1880
1885
  );
1881
1886
  }
1882
1887
  }
1888
+ /**
1889
+ * Get a download URL for a file that triggers browser download
1890
+ *
1891
+ * @param path - Path to the file in project storage
1892
+ * @param options - Download options including custom filename
1893
+ * @returns Promise resolving to download response with download URL
1894
+ *
1895
+ * @example
1896
+ * ```ts
1897
+ * // Download with original filename
1898
+ * const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg');
1899
+ * window.open(downloadUrl, '_blank');
1900
+ *
1901
+ * // Download with custom filename
1902
+ * const { downloadUrl } = await blink.storage.download(
1903
+ * 'images/photo.jpg',
1904
+ * { filename: 'my-photo.jpg' }
1905
+ * );
1906
+ *
1907
+ * // Create download link in React
1908
+ * <a href={downloadUrl} download={filename}>Download Image</a>
1909
+ * ```
1910
+ */
1911
+ async download(path, options = {}) {
1912
+ try {
1913
+ if (!path || typeof path !== "string" || !path.trim()) {
1914
+ throw new BlinkStorageError("Path must be a non-empty string");
1915
+ }
1916
+ const response = await this.httpClient.request(
1917
+ `/api/storage/${this.httpClient.projectId}/download`,
1918
+ {
1919
+ method: "GET",
1920
+ searchParams: {
1921
+ path: path.trim(),
1922
+ ...options.filename && { filename: options.filename }
1923
+ }
1924
+ }
1925
+ );
1926
+ if (response.data?.downloadUrl) {
1927
+ return {
1928
+ downloadUrl: response.data.downloadUrl,
1929
+ filename: response.data.filename || options.filename || path.split("/").pop() || "download",
1930
+ contentType: response.data.contentType,
1931
+ size: response.data.size
1932
+ };
1933
+ } else {
1934
+ throw new BlinkStorageError("Invalid response format: missing downloadUrl");
1935
+ }
1936
+ } catch (error) {
1937
+ if (error instanceof BlinkStorageError) {
1938
+ throw error;
1939
+ }
1940
+ if (error instanceof Error && "status" in error) {
1941
+ const status = error.status;
1942
+ if (status === 404) {
1943
+ throw new BlinkStorageError("File not found", 404);
1944
+ }
1945
+ if (status === 400) {
1946
+ throw new BlinkStorageError("Invalid request parameters", 400);
1947
+ }
1948
+ }
1949
+ throw new BlinkStorageError(
1950
+ `Download failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1951
+ void 0,
1952
+ { originalError: error }
1953
+ );
1954
+ }
1955
+ }
1883
1956
  /**
1884
1957
  * Remove one or more files from project storage
1885
1958
  *
@@ -2347,15 +2420,15 @@ var BlinkAIImpl = class {
2347
2420
  }
2348
2421
  }
2349
2422
  /**
2350
- * Generates images from text descriptions using AI image models.
2423
+ * Generates images from text descriptions using AI.
2351
2424
  *
2352
2425
  * @param options - Object containing:
2353
- * - `prompt`: Text description of the image to generate (required)
2354
- * - `size`: Image dimensions (e.g., "1024x1024", "512x512") - varies by model
2355
- * - `quality`: Image quality ("standard" or "hd")
2426
+ * - `prompt`: Text description of the desired image (required)
2427
+ * - `size`: Image dimensions (default: "1024x1024")
2428
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
2356
2429
  * - `n`: Number of images to generate (default: 1)
2357
- * - `response_format`: Output format ("url" or "b64_json")
2358
- * - Plus optional model, signal parameters
2430
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
2431
+ * - Plus optional signal parameter
2359
2432
  *
2360
2433
  * @example
2361
2434
  * ```ts
@@ -2368,29 +2441,25 @@ var BlinkAIImpl = class {
2368
2441
  * // High-quality image with specific size
2369
2442
  * const { data } = await blink.ai.generateImage({
2370
2443
  * prompt: "A futuristic city skyline with flying cars",
2371
- * size: "1792x1024",
2444
+ * size: "1536x1024",
2372
2445
  * quality: "hd",
2373
- * model: "dall-e-3"
2446
+ * background: "transparent"
2374
2447
  * });
2375
2448
  *
2376
2449
  * // Multiple images
2377
2450
  * const { data } = await blink.ai.generateImage({
2378
2451
  * prompt: "A cute robot mascot for a tech company",
2379
2452
  * n: 3,
2380
- * size: "1024x1024"
2453
+ * size: "1024x1024",
2454
+ * quality: "hd"
2381
2455
  * });
2382
2456
  * data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url));
2383
- *
2384
- * // Base64 format for direct embedding
2385
- * const { data } = await blink.ai.generateImage({
2386
- * prompt: "A minimalist logo design",
2387
- * response_format: "b64_json"
2388
- * });
2389
- * console.log("Base64 data:", data[0].b64_json);
2390
2457
  * ```
2391
2458
  *
2392
2459
  * @returns Promise<ImageGenerationResponse> - Object containing:
2393
- * - `data`: Array of generated images with url or b64_json
2460
+ * - `data`: Array of generated images with URLs
2461
+ * - `created`: Timestamp of generation
2462
+ * - `usage`: Token usage information
2394
2463
  */
2395
2464
  async generateImage(options) {
2396
2465
  try {
@@ -2400,11 +2469,12 @@ var BlinkAIImpl = class {
2400
2469
  const response = await this.httpClient.aiImage(
2401
2470
  options.prompt,
2402
2471
  {
2403
- model: options.model,
2472
+ model: "gpt-image-1",
2404
2473
  size: options.size,
2405
2474
  quality: options.quality,
2406
2475
  n: options.n,
2407
- response_format: options.response_format,
2476
+ background: options.background,
2477
+ response_format: "url",
2408
2478
  signal: options.signal
2409
2479
  }
2410
2480
  );
@@ -2424,10 +2494,8 @@ var BlinkAIImpl = class {
2424
2494
  return { url: item };
2425
2495
  } else if (item.url) {
2426
2496
  return item;
2427
- } else if (item.b64_json) {
2428
- return { b64_json: item.b64_json };
2429
2497
  } else {
2430
- return { url: item };
2498
+ throw new BlinkAIError("Invalid image response format");
2431
2499
  }
2432
2500
  });
2433
2501
  return imageResponse;
@@ -2442,6 +2510,129 @@ var BlinkAIImpl = class {
2442
2510
  );
2443
2511
  }
2444
2512
  }
2513
+ /**
2514
+ * Modifies existing images using AI with text prompts for image-to-image editing.
2515
+ *
2516
+ * @param options - Object containing:
2517
+ * - `images`: Array of public image URLs to modify (required, up to 16 images)
2518
+ * - `prompt`: Text description of desired modifications (required)
2519
+ * - `size`: Output image dimensions (default: "auto")
2520
+ * - `quality`: Image quality ("standard" or "hd", default: "standard")
2521
+ * - `n`: Number of output images to generate (default: 1)
2522
+ * - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
2523
+ * - Plus optional signal parameter
2524
+ *
2525
+ * @example
2526
+ * ```ts
2527
+ * // Professional headshots from casual photos
2528
+ * const { data } = await blink.ai.modifyImage({
2529
+ * images: [
2530
+ * "https://storage.example.com/user-photo-1.jpg",
2531
+ * "https://storage.example.com/user-photo-2.jpg"
2532
+ * ],
2533
+ * prompt: "Transform into professional business headshots with studio lighting",
2534
+ * quality: "hd",
2535
+ * n: 4
2536
+ * });
2537
+ * data.forEach((img, i) => console.log(`Headshot ${i+1}:`, img.url));
2538
+ *
2539
+ * // Artistic style transformation
2540
+ * const { data } = await blink.ai.modifyImage({
2541
+ * images: ["https://storage.example.com/portrait.jpg"],
2542
+ * prompt: "Transform into oil painting style with dramatic lighting",
2543
+ * quality: "hd",
2544
+ * size: "1024x1024"
2545
+ * });
2546
+ *
2547
+ * // Background replacement
2548
+ * const { data } = await blink.ai.modifyImage({
2549
+ * images: ["https://storage.example.com/product.jpg"],
2550
+ * prompt: "Remove background and place on clean white studio background",
2551
+ * background: "transparent",
2552
+ * n: 2
2553
+ * });
2554
+ *
2555
+ * // Batch processing multiple photos
2556
+ * const userPhotos = [
2557
+ * "https://storage.example.com/photo1.jpg",
2558
+ * "https://storage.example.com/photo2.jpg",
2559
+ * "https://storage.example.com/photo3.jpg"
2560
+ * ];
2561
+ * const { data } = await blink.ai.modifyImage({
2562
+ * images: userPhotos,
2563
+ * prompt: "Convert to black and white vintage style photographs",
2564
+ * quality: "hd"
2565
+ * });
2566
+ * ```
2567
+ *
2568
+ * @returns Promise<ImageGenerationResponse> - Object containing:
2569
+ * - `data`: Array of modified images with URLs
2570
+ * - `created`: Timestamp of generation
2571
+ * - `usage`: Token usage information
2572
+ */
2573
+ async modifyImage(options) {
2574
+ try {
2575
+ if (!options.prompt) {
2576
+ throw new BlinkAIError("Prompt is required");
2577
+ }
2578
+ if (!options.images || !Array.isArray(options.images) || options.images.length === 0) {
2579
+ throw new BlinkAIError("Images array is required and must contain at least one image URL");
2580
+ }
2581
+ if (options.images.length > 16) {
2582
+ throw new BlinkAIError("Maximum 16 images allowed");
2583
+ }
2584
+ for (let i = 0; i < options.images.length; i++) {
2585
+ const validation = this.validateImageUrl(options.images[i]);
2586
+ if (!validation.isValid) {
2587
+ throw new BlinkAIError(`Image ${i + 1}: ${validation.error}`);
2588
+ }
2589
+ }
2590
+ const response = await this.httpClient.aiImage(
2591
+ options.prompt,
2592
+ // Non-null assertion since we validated above
2593
+ {
2594
+ model: "gpt-image-1",
2595
+ images: options.images,
2596
+ size: options.size,
2597
+ quality: options.quality,
2598
+ n: options.n,
2599
+ background: options.background,
2600
+ response_format: "url",
2601
+ signal: options.signal
2602
+ }
2603
+ );
2604
+ let imageResponse;
2605
+ if (response.data?.result?.data) {
2606
+ imageResponse = response.data.result;
2607
+ } else if (response.data?.data) {
2608
+ imageResponse = response.data;
2609
+ } else {
2610
+ throw new BlinkAIError("Invalid response format: missing image data");
2611
+ }
2612
+ if (!Array.isArray(imageResponse.data)) {
2613
+ throw new BlinkAIError("Invalid response format: data should be an array");
2614
+ }
2615
+ imageResponse.data = imageResponse.data.map((item) => {
2616
+ if (typeof item === "string") {
2617
+ return { url: item };
2618
+ } else if (item.url) {
2619
+ return item;
2620
+ } else {
2621
+ throw new BlinkAIError("Invalid image response format");
2622
+ }
2623
+ });
2624
+ return imageResponse;
2625
+ } catch (error) {
2626
+ if (error instanceof BlinkAIError) {
2627
+ throw error;
2628
+ }
2629
+ throw new BlinkAIError(
2630
+ `Image modification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
2631
+ void 0,
2632
+ { originalError: error }
2633
+ );
2634
+ }
2635
+ }
2445
2636
  /**
2446
2637
  * Converts text to speech using AI voice synthesis models.
2447
2638
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.14.12",
3
+ "version": "0.16.0",
4
4
  "description": "Blink TypeScript SDK for client-side applications - Zero-boilerplate CRUD + auth + AI + analytics + notifications for modern SaaS/AI apps",
5
5
  "keywords": [
6
6
  "blink",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {},
52
52
  "devDependencies": {
53
- "@blink/core": "workspace:*",
53
+ "@blink/core": "0.4.0",
54
54
  "tsup": "^8.0.0",
55
55
  "typescript": "^5.0.0"
56
56
  },