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