@blinkdotnew/sdk 0.14.13 → 0.17.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 +27 -4
- package/dist/index.d.mts +179 -23
- package/dist/index.d.ts +179 -23
- package/dist/index.js +400 -43
- package/dist/index.mjs +400 -43
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1817,20 +1817,27 @@ var BlinkStorageImpl = class {
|
|
|
1817
1817
|
* Upload a file to project storage
|
|
1818
1818
|
*
|
|
1819
1819
|
* @param file - File, Blob, or Buffer to upload
|
|
1820
|
-
* @param path - Destination path within project storage
|
|
1820
|
+
* @param path - Destination path within project storage (extension will be auto-corrected to match file type)
|
|
1821
1821
|
* @param options - Upload options including upsert and progress callback
|
|
1822
1822
|
* @returns Promise resolving to upload response with public URL
|
|
1823
1823
|
*
|
|
1824
1824
|
* @example
|
|
1825
1825
|
* ```ts
|
|
1826
|
+
* // Extension automatically corrected to match actual file type
|
|
1826
1827
|
* const { publicUrl } = await blink.storage.upload(
|
|
1827
|
-
*
|
|
1828
|
-
* `avatars/${user.id}
|
|
1829
|
-
* {
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1828
|
+
* pngFile,
|
|
1829
|
+
* `avatars/${user.id}`, // No extension needed!
|
|
1830
|
+
* { upsert: true }
|
|
1831
|
+
* );
|
|
1832
|
+
* // If file is PNG, final path will be: avatars/user123.png
|
|
1833
|
+
*
|
|
1834
|
+
* // Or with extension (will be corrected if wrong)
|
|
1835
|
+
* const { publicUrl } = await blink.storage.upload(
|
|
1836
|
+
* pngFile,
|
|
1837
|
+
* `avatars/${user.id}.jpg`, // Wrong extension
|
|
1838
|
+
* { upsert: true }
|
|
1833
1839
|
* );
|
|
1840
|
+
* // Final path will be: avatars/user123.png (auto-corrected!)
|
|
1834
1841
|
* ```
|
|
1835
1842
|
*/
|
|
1836
1843
|
async upload(file, path, options = {}) {
|
|
@@ -1851,10 +1858,12 @@ var BlinkStorageImpl = class {
|
|
|
1851
1858
|
if (fileSize > maxSize) {
|
|
1852
1859
|
throw new BlinkStorageError(`File size (${Math.round(fileSize / 1024 / 1024)}MB) exceeds maximum allowed size (50MB)`);
|
|
1853
1860
|
}
|
|
1861
|
+
const { correctedPath, detectedContentType } = await this.detectFileTypeAndCorrectPath(file, path);
|
|
1854
1862
|
const response = await this.httpClient.uploadFile(
|
|
1855
1863
|
`/api/storage/${this.httpClient.projectId}/upload`,
|
|
1856
1864
|
file,
|
|
1857
|
-
|
|
1865
|
+
correctedPath,
|
|
1866
|
+
// Use corrected path with proper extension
|
|
1858
1867
|
{
|
|
1859
1868
|
upsert: options.upsert,
|
|
1860
1869
|
onProgress: options.onProgress
|
|
@@ -1887,6 +1896,200 @@ var BlinkStorageImpl = class {
|
|
|
1887
1896
|
);
|
|
1888
1897
|
}
|
|
1889
1898
|
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Detect file type from actual file content and correct path extension
|
|
1901
|
+
* This ensures the path extension always matches the actual file type
|
|
1902
|
+
*/
|
|
1903
|
+
async detectFileTypeAndCorrectPath(file, originalPath) {
|
|
1904
|
+
try {
|
|
1905
|
+
const fileSignature = await this.getFileSignature(file);
|
|
1906
|
+
const detectedType = this.detectFileTypeFromSignature(fileSignature);
|
|
1907
|
+
let detectedContentType = detectedType.mimeType;
|
|
1908
|
+
let detectedExtension = detectedType.extension;
|
|
1909
|
+
if (!detectedContentType && file instanceof File && file.type) {
|
|
1910
|
+
detectedContentType = file.type;
|
|
1911
|
+
detectedExtension = this.getExtensionFromMimeType(file.type);
|
|
1912
|
+
}
|
|
1913
|
+
if (!detectedContentType) {
|
|
1914
|
+
detectedContentType = "application/octet-stream";
|
|
1915
|
+
detectedExtension = "bin";
|
|
1916
|
+
}
|
|
1917
|
+
const pathParts = originalPath.split("/");
|
|
1918
|
+
const fileName = pathParts[pathParts.length - 1];
|
|
1919
|
+
const directory = pathParts.slice(0, -1).join("/");
|
|
1920
|
+
if (!fileName) {
|
|
1921
|
+
throw new Error("Invalid path: filename cannot be empty");
|
|
1922
|
+
}
|
|
1923
|
+
const nameWithoutExt = fileName.includes(".") ? fileName.substring(0, fileName.lastIndexOf(".")) : fileName;
|
|
1924
|
+
const correctedFileName = `${nameWithoutExt}.${detectedExtension}`;
|
|
1925
|
+
const correctedPath = directory ? `${directory}/${correctedFileName}` : correctedFileName;
|
|
1926
|
+
return {
|
|
1927
|
+
correctedPath,
|
|
1928
|
+
detectedContentType
|
|
1929
|
+
};
|
|
1930
|
+
} catch (error) {
|
|
1931
|
+
console.warn("File type detection failed, using original path:", error);
|
|
1932
|
+
return {
|
|
1933
|
+
correctedPath: originalPath,
|
|
1934
|
+
detectedContentType: "application/octet-stream"
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Get the first few bytes of a file to analyze its signature
|
|
1940
|
+
*/
|
|
1941
|
+
async getFileSignature(file) {
|
|
1942
|
+
const bytesToRead = 12;
|
|
1943
|
+
if (typeof Buffer !== "undefined" && file instanceof Buffer) {
|
|
1944
|
+
return new Uint8Array(file.slice(0, bytesToRead));
|
|
1945
|
+
}
|
|
1946
|
+
if (file instanceof File || file instanceof Blob) {
|
|
1947
|
+
const slice = file.slice(0, bytesToRead);
|
|
1948
|
+
const arrayBuffer = await slice.arrayBuffer();
|
|
1949
|
+
return new Uint8Array(arrayBuffer);
|
|
1950
|
+
}
|
|
1951
|
+
throw new Error("Unsupported file type for signature detection");
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Detect file type from file signature (magic numbers)
|
|
1955
|
+
* This is the most reliable way to detect actual file type
|
|
1956
|
+
*/
|
|
1957
|
+
detectFileTypeFromSignature(signature) {
|
|
1958
|
+
const hex = Array.from(signature).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1959
|
+
const signatures = {
|
|
1960
|
+
// Images
|
|
1961
|
+
"ffd8ff": { mimeType: "image/jpeg", extension: "jpg" },
|
|
1962
|
+
"89504e47": { mimeType: "image/png", extension: "png" },
|
|
1963
|
+
"47494638": { mimeType: "image/gif", extension: "gif" },
|
|
1964
|
+
"52494646": { mimeType: "image/webp", extension: "webp" },
|
|
1965
|
+
// RIFF (WebP container)
|
|
1966
|
+
"424d": { mimeType: "image/bmp", extension: "bmp" },
|
|
1967
|
+
"49492a00": { mimeType: "image/tiff", extension: "tiff" },
|
|
1968
|
+
"4d4d002a": { mimeType: "image/tiff", extension: "tiff" },
|
|
1969
|
+
// Documents
|
|
1970
|
+
"25504446": { mimeType: "application/pdf", extension: "pdf" },
|
|
1971
|
+
"504b0304": { mimeType: "application/zip", extension: "zip" },
|
|
1972
|
+
// Also used by docx, xlsx
|
|
1973
|
+
"d0cf11e0": { mimeType: "application/msword", extension: "doc" },
|
|
1974
|
+
// Audio
|
|
1975
|
+
"494433": { mimeType: "audio/mpeg", extension: "mp3" },
|
|
1976
|
+
"664c6143": { mimeType: "audio/flac", extension: "flac" },
|
|
1977
|
+
"4f676753": { mimeType: "audio/ogg", extension: "ogg" },
|
|
1978
|
+
// Video
|
|
1979
|
+
"000000": { mimeType: "video/mp4", extension: "mp4" },
|
|
1980
|
+
// ftyp box
|
|
1981
|
+
"1a45dfa3": { mimeType: "video/webm", extension: "webm" },
|
|
1982
|
+
// Text
|
|
1983
|
+
"efbbbf": { mimeType: "text/plain", extension: "txt" }
|
|
1984
|
+
// UTF-8 BOM
|
|
1985
|
+
};
|
|
1986
|
+
for (const [sig, type] of Object.entries(signatures)) {
|
|
1987
|
+
if (hex.startsWith(sig)) {
|
|
1988
|
+
return type;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
if (hex.startsWith("52494646") && hex.substring(16, 24) === "57454250") {
|
|
1992
|
+
return { mimeType: "image/webp", extension: "webp" };
|
|
1993
|
+
}
|
|
1994
|
+
if (hex.substring(8, 16) === "66747970") {
|
|
1995
|
+
return { mimeType: "video/mp4", extension: "mp4" };
|
|
1996
|
+
}
|
|
1997
|
+
return { mimeType: "", extension: "" };
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* Get file extension from MIME type as fallback
|
|
2001
|
+
*/
|
|
2002
|
+
getExtensionFromMimeType(mimeType) {
|
|
2003
|
+
const mimeToExt = {
|
|
2004
|
+
"image/jpeg": "jpg",
|
|
2005
|
+
"image/png": "png",
|
|
2006
|
+
"image/gif": "gif",
|
|
2007
|
+
"image/webp": "webp",
|
|
2008
|
+
"image/bmp": "bmp",
|
|
2009
|
+
"image/svg+xml": "svg",
|
|
2010
|
+
"application/pdf": "pdf",
|
|
2011
|
+
"text/plain": "txt",
|
|
2012
|
+
"text/html": "html",
|
|
2013
|
+
"text/css": "css",
|
|
2014
|
+
"application/javascript": "js",
|
|
2015
|
+
"application/json": "json",
|
|
2016
|
+
"audio/mpeg": "mp3",
|
|
2017
|
+
"audio/wav": "wav",
|
|
2018
|
+
"audio/ogg": "ogg",
|
|
2019
|
+
"video/mp4": "mp4",
|
|
2020
|
+
"video/webm": "webm",
|
|
2021
|
+
"application/zip": "zip"
|
|
2022
|
+
};
|
|
2023
|
+
return mimeToExt[mimeType] || "bin";
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Get a download URL for a file that triggers browser download
|
|
2027
|
+
*
|
|
2028
|
+
* @param path - Path to the file in project storage
|
|
2029
|
+
* @param options - Download options including custom filename
|
|
2030
|
+
* @returns Promise resolving to download response with download URL
|
|
2031
|
+
*
|
|
2032
|
+
* @example
|
|
2033
|
+
* ```ts
|
|
2034
|
+
* // Download with original filename
|
|
2035
|
+
* const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg');
|
|
2036
|
+
* window.open(downloadUrl, '_blank');
|
|
2037
|
+
*
|
|
2038
|
+
* // Download with custom filename
|
|
2039
|
+
* const { downloadUrl } = await blink.storage.download(
|
|
2040
|
+
* 'images/photo.jpg',
|
|
2041
|
+
* { filename: 'my-photo.jpg' }
|
|
2042
|
+
* );
|
|
2043
|
+
*
|
|
2044
|
+
* // Create download link in React
|
|
2045
|
+
* <a href={downloadUrl} download={filename}>Download Image</a>
|
|
2046
|
+
* ```
|
|
2047
|
+
*/
|
|
2048
|
+
async download(path, options = {}) {
|
|
2049
|
+
try {
|
|
2050
|
+
if (!path || typeof path !== "string" || !path.trim()) {
|
|
2051
|
+
throw new BlinkStorageError("Path must be a non-empty string");
|
|
2052
|
+
}
|
|
2053
|
+
const response = await this.httpClient.request(
|
|
2054
|
+
`/api/storage/${this.httpClient.projectId}/download`,
|
|
2055
|
+
{
|
|
2056
|
+
method: "GET",
|
|
2057
|
+
searchParams: {
|
|
2058
|
+
path: path.trim(),
|
|
2059
|
+
...options.filename && { filename: options.filename }
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
);
|
|
2063
|
+
if (response.data?.downloadUrl) {
|
|
2064
|
+
return {
|
|
2065
|
+
downloadUrl: response.data.downloadUrl,
|
|
2066
|
+
filename: response.data.filename || options.filename || path.split("/").pop() || "download",
|
|
2067
|
+
contentType: response.data.contentType,
|
|
2068
|
+
size: response.data.size
|
|
2069
|
+
};
|
|
2070
|
+
} else {
|
|
2071
|
+
throw new BlinkStorageError("Invalid response format: missing downloadUrl");
|
|
2072
|
+
}
|
|
2073
|
+
} catch (error) {
|
|
2074
|
+
if (error instanceof BlinkStorageError) {
|
|
2075
|
+
throw error;
|
|
2076
|
+
}
|
|
2077
|
+
if (error instanceof Error && "status" in error) {
|
|
2078
|
+
const status = error.status;
|
|
2079
|
+
if (status === 404) {
|
|
2080
|
+
throw new BlinkStorageError("File not found", 404);
|
|
2081
|
+
}
|
|
2082
|
+
if (status === 400) {
|
|
2083
|
+
throw new BlinkStorageError("Invalid request parameters", 400);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
throw new BlinkStorageError(
|
|
2087
|
+
`Download failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2088
|
+
void 0,
|
|
2089
|
+
{ originalError: error }
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
1890
2093
|
/**
|
|
1891
2094
|
* Remove one or more files from project storage
|
|
1892
2095
|
*
|
|
@@ -2354,15 +2557,15 @@ var BlinkAIImpl = class {
|
|
|
2354
2557
|
}
|
|
2355
2558
|
}
|
|
2356
2559
|
/**
|
|
2357
|
-
* Generates images from text descriptions using AI
|
|
2560
|
+
* Generates images from text descriptions using AI.
|
|
2358
2561
|
*
|
|
2359
2562
|
* @param options - Object containing:
|
|
2360
|
-
* - `prompt`: Text description of the image
|
|
2361
|
-
* - `size`: Image dimensions (
|
|
2362
|
-
* - `quality`: Image quality ("standard" or "hd")
|
|
2563
|
+
* - `prompt`: Text description of the desired image (required)
|
|
2564
|
+
* - `size`: Image dimensions (default: "1024x1024")
|
|
2565
|
+
* - `quality`: Image quality ("standard" or "hd", default: "standard")
|
|
2363
2566
|
* - `n`: Number of images to generate (default: 1)
|
|
2364
|
-
* - `
|
|
2365
|
-
* - Plus optional
|
|
2567
|
+
* - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
|
|
2568
|
+
* - Plus optional signal parameter
|
|
2366
2569
|
*
|
|
2367
2570
|
* @example
|
|
2368
2571
|
* ```ts
|
|
@@ -2375,29 +2578,25 @@ var BlinkAIImpl = class {
|
|
|
2375
2578
|
* // High-quality image with specific size
|
|
2376
2579
|
* const { data } = await blink.ai.generateImage({
|
|
2377
2580
|
* prompt: "A futuristic city skyline with flying cars",
|
|
2378
|
-
* size: "
|
|
2581
|
+
* size: "1536x1024",
|
|
2379
2582
|
* quality: "hd",
|
|
2380
|
-
*
|
|
2583
|
+
* background: "transparent"
|
|
2381
2584
|
* });
|
|
2382
2585
|
*
|
|
2383
2586
|
* // Multiple images
|
|
2384
2587
|
* const { data } = await blink.ai.generateImage({
|
|
2385
2588
|
* prompt: "A cute robot mascot for a tech company",
|
|
2386
2589
|
* n: 3,
|
|
2387
|
-
* size: "1024x1024"
|
|
2590
|
+
* size: "1024x1024",
|
|
2591
|
+
* quality: "hd"
|
|
2388
2592
|
* });
|
|
2389
2593
|
* data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url));
|
|
2390
|
-
*
|
|
2391
|
-
* // Base64 format for direct embedding
|
|
2392
|
-
* const { data } = await blink.ai.generateImage({
|
|
2393
|
-
* prompt: "A minimalist logo design",
|
|
2394
|
-
* response_format: "b64_json"
|
|
2395
|
-
* });
|
|
2396
|
-
* console.log("Base64 data:", data[0].b64_json);
|
|
2397
2594
|
* ```
|
|
2398
2595
|
*
|
|
2399
2596
|
* @returns Promise<ImageGenerationResponse> - Object containing:
|
|
2400
|
-
* - `data`: Array of generated images with
|
|
2597
|
+
* - `data`: Array of generated images with URLs
|
|
2598
|
+
* - `created`: Timestamp of generation
|
|
2599
|
+
* - `usage`: Token usage information
|
|
2401
2600
|
*/
|
|
2402
2601
|
async generateImage(options) {
|
|
2403
2602
|
try {
|
|
@@ -2407,11 +2606,12 @@ var BlinkAIImpl = class {
|
|
|
2407
2606
|
const response = await this.httpClient.aiImage(
|
|
2408
2607
|
options.prompt,
|
|
2409
2608
|
{
|
|
2410
|
-
model:
|
|
2609
|
+
model: "gpt-image-1",
|
|
2411
2610
|
size: options.size,
|
|
2412
2611
|
quality: options.quality,
|
|
2413
2612
|
n: options.n,
|
|
2414
|
-
|
|
2613
|
+
background: options.background,
|
|
2614
|
+
response_format: "url",
|
|
2415
2615
|
signal: options.signal
|
|
2416
2616
|
}
|
|
2417
2617
|
);
|
|
@@ -2431,10 +2631,8 @@ var BlinkAIImpl = class {
|
|
|
2431
2631
|
return { url: item };
|
|
2432
2632
|
} else if (item.url) {
|
|
2433
2633
|
return item;
|
|
2434
|
-
} else if (item.b64_json) {
|
|
2435
|
-
return { b64_json: item.b64_json };
|
|
2436
2634
|
} else {
|
|
2437
|
-
|
|
2635
|
+
throw new BlinkAIError("Invalid image response format");
|
|
2438
2636
|
}
|
|
2439
2637
|
});
|
|
2440
2638
|
return imageResponse;
|
|
@@ -2449,6 +2647,129 @@ var BlinkAIImpl = class {
|
|
|
2449
2647
|
);
|
|
2450
2648
|
}
|
|
2451
2649
|
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Modifies existing images using AI with text prompts for image-to-image editing.
|
|
2652
|
+
*
|
|
2653
|
+
* @param options - Object containing:
|
|
2654
|
+
* - `images`: Array of public image URLs to modify (required, up to 16 images)
|
|
2655
|
+
* - `prompt`: Text description of desired modifications (required)
|
|
2656
|
+
* - `size`: Output image dimensions (default: "auto")
|
|
2657
|
+
* - `quality`: Image quality ("standard" or "hd", default: "standard")
|
|
2658
|
+
* - `n`: Number of output images to generate (default: 1)
|
|
2659
|
+
* - `background`: Background handling ("auto", "transparent", "opaque", default: "auto")
|
|
2660
|
+
* - Plus optional signal parameter
|
|
2661
|
+
*
|
|
2662
|
+
* @example
|
|
2663
|
+
* ```ts
|
|
2664
|
+
* // Professional headshots from casual photos
|
|
2665
|
+
* const { data } = await blink.ai.modifyImage({
|
|
2666
|
+
* images: [
|
|
2667
|
+
* "https://storage.example.com/user-photo-1.jpg",
|
|
2668
|
+
* "https://storage.example.com/user-photo-2.jpg"
|
|
2669
|
+
* ],
|
|
2670
|
+
* prompt: "Transform into professional business headshots with studio lighting",
|
|
2671
|
+
* quality: "hd",
|
|
2672
|
+
* n: 4
|
|
2673
|
+
* });
|
|
2674
|
+
* data.forEach((img, i) => console.log(`Headshot ${i+1}:`, img.url));
|
|
2675
|
+
*
|
|
2676
|
+
* // Artistic style transformation
|
|
2677
|
+
* const { data } = await blink.ai.modifyImage({
|
|
2678
|
+
* images: ["https://storage.example.com/portrait.jpg"],
|
|
2679
|
+
* prompt: "Transform into oil painting style with dramatic lighting",
|
|
2680
|
+
* quality: "hd",
|
|
2681
|
+
* size: "1024x1024"
|
|
2682
|
+
* });
|
|
2683
|
+
*
|
|
2684
|
+
* // Background replacement
|
|
2685
|
+
* const { data } = await blink.ai.modifyImage({
|
|
2686
|
+
* images: ["https://storage.example.com/product.jpg"],
|
|
2687
|
+
* prompt: "Remove background and place on clean white studio background",
|
|
2688
|
+
* background: "transparent",
|
|
2689
|
+
* n: 2
|
|
2690
|
+
* });
|
|
2691
|
+
*
|
|
2692
|
+
* // Batch processing multiple photos
|
|
2693
|
+
* const userPhotos = [
|
|
2694
|
+
* "https://storage.example.com/photo1.jpg",
|
|
2695
|
+
* "https://storage.example.com/photo2.jpg",
|
|
2696
|
+
* "https://storage.example.com/photo3.jpg"
|
|
2697
|
+
* ];
|
|
2698
|
+
* const { data } = await blink.ai.modifyImage({
|
|
2699
|
+
* images: userPhotos,
|
|
2700
|
+
* prompt: "Convert to black and white vintage style photographs",
|
|
2701
|
+
* quality: "hd"
|
|
2702
|
+
* });
|
|
2703
|
+
* ```
|
|
2704
|
+
*
|
|
2705
|
+
* @returns Promise<ImageGenerationResponse> - Object containing:
|
|
2706
|
+
* - `data`: Array of modified images with URLs
|
|
2707
|
+
* - `created`: Timestamp of generation
|
|
2708
|
+
* - `usage`: Token usage information
|
|
2709
|
+
*/
|
|
2710
|
+
async modifyImage(options) {
|
|
2711
|
+
try {
|
|
2712
|
+
if (!options.prompt) {
|
|
2713
|
+
throw new BlinkAIError("Prompt is required");
|
|
2714
|
+
}
|
|
2715
|
+
if (!options.images || !Array.isArray(options.images) || options.images.length === 0) {
|
|
2716
|
+
throw new BlinkAIError("Images array is required and must contain at least one image URL");
|
|
2717
|
+
}
|
|
2718
|
+
if (options.images.length > 16) {
|
|
2719
|
+
throw new BlinkAIError("Maximum 16 images allowed");
|
|
2720
|
+
}
|
|
2721
|
+
for (let i = 0; i < options.images.length; i++) {
|
|
2722
|
+
const validation = this.validateImageUrl(options.images[i]);
|
|
2723
|
+
if (!validation.isValid) {
|
|
2724
|
+
throw new BlinkAIError(`Image ${i + 1}: ${validation.error}`);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
const response = await this.httpClient.aiImage(
|
|
2728
|
+
options.prompt,
|
|
2729
|
+
// Non-null assertion since we validated above
|
|
2730
|
+
{
|
|
2731
|
+
model: "gpt-image-1",
|
|
2732
|
+
images: options.images,
|
|
2733
|
+
size: options.size,
|
|
2734
|
+
quality: options.quality,
|
|
2735
|
+
n: options.n,
|
|
2736
|
+
background: options.background,
|
|
2737
|
+
response_format: "url",
|
|
2738
|
+
signal: options.signal
|
|
2739
|
+
}
|
|
2740
|
+
);
|
|
2741
|
+
let imageResponse;
|
|
2742
|
+
if (response.data?.result?.data) {
|
|
2743
|
+
imageResponse = response.data.result;
|
|
2744
|
+
} else if (response.data?.data) {
|
|
2745
|
+
imageResponse = response.data;
|
|
2746
|
+
} else {
|
|
2747
|
+
throw new BlinkAIError("Invalid response format: missing image data");
|
|
2748
|
+
}
|
|
2749
|
+
if (!Array.isArray(imageResponse.data)) {
|
|
2750
|
+
throw new BlinkAIError("Invalid response format: data should be an array");
|
|
2751
|
+
}
|
|
2752
|
+
imageResponse.data = imageResponse.data.map((item) => {
|
|
2753
|
+
if (typeof item === "string") {
|
|
2754
|
+
return { url: item };
|
|
2755
|
+
} else if (item.url) {
|
|
2756
|
+
return item;
|
|
2757
|
+
} else {
|
|
2758
|
+
throw new BlinkAIError("Invalid image response format");
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
return imageResponse;
|
|
2762
|
+
} catch (error) {
|
|
2763
|
+
if (error instanceof BlinkAIError) {
|
|
2764
|
+
throw error;
|
|
2765
|
+
}
|
|
2766
|
+
throw new BlinkAIError(
|
|
2767
|
+
`Image modification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2768
|
+
void 0,
|
|
2769
|
+
{ originalError: error }
|
|
2770
|
+
);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2452
2773
|
/**
|
|
2453
2774
|
* Converts text to speech using AI voice synthesis models.
|
|
2454
2775
|
*
|
|
@@ -2977,6 +3298,15 @@ var BlinkRealtimeChannel = class {
|
|
|
2977
3298
|
} catch (err) {
|
|
2978
3299
|
}
|
|
2979
3300
|
};
|
|
3301
|
+
const originalTimeout = timeout;
|
|
3302
|
+
const cleanupTimeout = setTimeout(() => {
|
|
3303
|
+
if (this.websocket) {
|
|
3304
|
+
this.websocket.removeEventListener("message", handleResponse);
|
|
3305
|
+
}
|
|
3306
|
+
reject(new BlinkRealtimeError("Message send timeout - no response from server"));
|
|
3307
|
+
}, 1e4);
|
|
3308
|
+
queuedMessage.timeout = cleanupTimeout;
|
|
3309
|
+
clearTimeout(originalTimeout);
|
|
2980
3310
|
this.websocket.addEventListener("message", handleResponse);
|
|
2981
3311
|
this.websocket.send(message);
|
|
2982
3312
|
}
|
|
@@ -3393,6 +3723,15 @@ var BlinkAnalyticsImpl = class {
|
|
|
3393
3723
|
this.enabled = false;
|
|
3394
3724
|
this.clearTimer();
|
|
3395
3725
|
}
|
|
3726
|
+
/**
|
|
3727
|
+
* Cleanup analytics instance (remove from global tracking)
|
|
3728
|
+
*/
|
|
3729
|
+
destroy() {
|
|
3730
|
+
this.disable();
|
|
3731
|
+
if (typeof window !== "undefined") {
|
|
3732
|
+
window.__blinkAnalyticsInstances?.delete(this);
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3396
3735
|
/**
|
|
3397
3736
|
* Enable analytics tracking
|
|
3398
3737
|
*/
|
|
@@ -3559,19 +3898,37 @@ var BlinkAnalyticsImpl = class {
|
|
|
3559
3898
|
}
|
|
3560
3899
|
}
|
|
3561
3900
|
setupRouteChangeListener() {
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3901
|
+
if (!window.__blinkAnalyticsSetup) {
|
|
3902
|
+
const originalPushState = history.pushState;
|
|
3903
|
+
const originalReplaceState = history.replaceState;
|
|
3904
|
+
const analyticsInstances = /* @__PURE__ */ new Set();
|
|
3905
|
+
window.__blinkAnalyticsInstances = analyticsInstances;
|
|
3906
|
+
history.pushState = (...args) => {
|
|
3907
|
+
originalPushState.apply(history, args);
|
|
3908
|
+
analyticsInstances.forEach((instance) => {
|
|
3909
|
+
if (instance.isEnabled()) {
|
|
3910
|
+
instance.log("pageview");
|
|
3911
|
+
}
|
|
3912
|
+
});
|
|
3913
|
+
};
|
|
3914
|
+
history.replaceState = (...args) => {
|
|
3915
|
+
originalReplaceState.apply(history, args);
|
|
3916
|
+
analyticsInstances.forEach((instance) => {
|
|
3917
|
+
if (instance.isEnabled()) {
|
|
3918
|
+
instance.log("pageview");
|
|
3919
|
+
}
|
|
3920
|
+
});
|
|
3921
|
+
};
|
|
3922
|
+
window.addEventListener("popstate", () => {
|
|
3923
|
+
analyticsInstances.forEach((instance) => {
|
|
3924
|
+
if (instance.isEnabled()) {
|
|
3925
|
+
instance.log("pageview");
|
|
3926
|
+
}
|
|
3927
|
+
});
|
|
3928
|
+
});
|
|
3929
|
+
window.__blinkAnalyticsSetup = true;
|
|
3930
|
+
}
|
|
3931
|
+
window.__blinkAnalyticsInstances?.add(this);
|
|
3575
3932
|
}
|
|
3576
3933
|
setupUnloadListener() {
|
|
3577
3934
|
window.addEventListener("pagehide", () => {
|