@tanstack/ai-lovable 0.2.9 → 0.2.12
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.
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { base64ToArrayBuffer } from "@tanstack/ai-utils";
|
|
2
|
+
import { isFileSource, unsupportedFileSourceError } from "@tanstack/ai";
|
|
2
3
|
//#region src/image/image-input-to-file.ts
|
|
3
4
|
var DEFAULT_MIME = "image/png";
|
|
4
5
|
var MIME_TO_EXT = {
|
|
@@ -22,6 +23,7 @@ function ensureFileSupport() {
|
|
|
22
23
|
*/
|
|
23
24
|
async function imagePartToFile(part, fallbackName, allowUrlFetch, abortSignal) {
|
|
24
25
|
ensureFileSupport();
|
|
26
|
+
if (isFileSource(part.source)) throw unsupportedFileSourceError("lovable", "on the edit / input_reference endpoints, which require uploaded bytes — pass a data: URI or inline image data");
|
|
25
27
|
if (part.source.type === "data") {
|
|
26
28
|
const mimeType = part.source.mimeType || DEFAULT_MIME;
|
|
27
29
|
const bytes = base64ToArrayBuffer(part.source.value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-input-to-file.js","names":[],"sources":["../../../src/image/image-input-to-file.ts"],"sourcesContent":["import { base64ToArrayBuffer } from '@tanstack/ai-utils'\nimport type { ImagePart, MediaInputMetadata } from '@tanstack/ai'\n\nconst DEFAULT_MIME = 'image/png'\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/jpg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n}\n\nfunction extForMime(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? mimeType.split('/')[1] ?? 'png'\n}\n\nfunction ensureFileSupport(): void {\n if (typeof File === 'undefined') {\n throw new Error(\n '`File` is not available in this environment. ' +\n 'Image-conditioned generation requires Node 20+ or a browser context.',\n )\n }\n}\n\n/**\n * Convert a TanStack `ImagePart` into an OpenAI-compatible `File`.\n *\n * HTTP(S) URLs are fetched only when `allowUrlFetch` is set. The gateway\n * edit and `input_reference` endpoints need uploaded bytes, not a URL.\n */\nexport async function imagePartToFile(\n part: ImagePart<MediaInputMetadata>,\n fallbackName: string,\n allowUrlFetch: boolean,\n abortSignal?: AbortSignal,\n): Promise<File> {\n ensureFileSupport()\n\n if (part.source.type === 'data') {\n const mimeType = part.source.mimeType || DEFAULT_MIME\n const bytes = base64ToArrayBuffer(part.source.value)\n return new File([bytes], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n }\n\n if (/^https?:\\/\\//i.test(part.source.value) && !allowUrlFetch) {\n throw new Error(\n `lovable: HTTP(S) URL image inputs are not fetched by default because ` +\n `the edit / input_reference endpoints require uploaded bytes, so ` +\n `the image would be downloaded and buffered in memory (risking OOM on ` +\n `constrained runtimes). Pass a data: URI, or set \\`allowUrlFetch: true\\` ` +\n `on the adapter config to opt into fetching. URL: ${part.source.value}`,\n )\n }\n\n const response = await fetch(\n part.source.value,\n abortSignal ? { signal: abortSignal } : undefined,\n )\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image input (${response.status} ${response.statusText}): ${part.source.value}`,\n )\n }\n const blob = await response.blob()\n const mimeType =\n part.source.mimeType || blob.type || inferMimeFromUrl(part.source.value)\n return new File([blob], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n}\n\nfunction inferMimeFromUrl(url: string): string {\n const match = url.match(/\\.(png|jpe?g|webp|gif)(?:\\?|#|$)/i)\n if (!match || !match[1]) return DEFAULT_MIME\n const ext = match[1].toLowerCase()\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n return `image/${ext}`\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"image-input-to-file.js","names":[],"sources":["../../../src/image/image-input-to-file.ts"],"sourcesContent":["import { base64ToArrayBuffer } from '@tanstack/ai-utils'\nimport { isFileSource, unsupportedFileSourceError } from '@tanstack/ai'\nimport type { ImagePart, MediaInputMetadata } from '@tanstack/ai'\n\nconst DEFAULT_MIME = 'image/png'\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/jpg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n}\n\nfunction extForMime(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? mimeType.split('/')[1] ?? 'png'\n}\n\nfunction ensureFileSupport(): void {\n if (typeof File === 'undefined') {\n throw new Error(\n '`File` is not available in this environment. ' +\n 'Image-conditioned generation requires Node 20+ or a browser context.',\n )\n }\n}\n\n/**\n * Convert a TanStack `ImagePart` into an OpenAI-compatible `File`.\n *\n * HTTP(S) URLs are fetched only when `allowUrlFetch` is set. The gateway\n * edit and `input_reference` endpoints need uploaded bytes, not a URL.\n */\nexport async function imagePartToFile(\n part: ImagePart<MediaInputMetadata>,\n fallbackName: string,\n allowUrlFetch: boolean,\n abortSignal?: AbortSignal,\n): Promise<File> {\n ensureFileSupport()\n\n if (isFileSource(part.source)) {\n // These endpoints are multipart and require the actual bytes; there is no\n // \"reference an uploaded handle\" option, and lovable has no Files API.\n throw unsupportedFileSourceError(\n 'lovable',\n 'on the edit / input_reference endpoints, which require uploaded bytes — pass a data: URI or inline image data',\n )\n }\n\n if (part.source.type === 'data') {\n const mimeType = part.source.mimeType || DEFAULT_MIME\n const bytes = base64ToArrayBuffer(part.source.value)\n return new File([bytes], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n }\n\n if (/^https?:\\/\\//i.test(part.source.value) && !allowUrlFetch) {\n throw new Error(\n `lovable: HTTP(S) URL image inputs are not fetched by default because ` +\n `the edit / input_reference endpoints require uploaded bytes, so ` +\n `the image would be downloaded and buffered in memory (risking OOM on ` +\n `constrained runtimes). Pass a data: URI, or set \\`allowUrlFetch: true\\` ` +\n `on the adapter config to opt into fetching. URL: ${part.source.value}`,\n )\n }\n\n const response = await fetch(\n part.source.value,\n abortSignal ? { signal: abortSignal } : undefined,\n )\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image input (${response.status} ${response.statusText}): ${part.source.value}`,\n )\n }\n const blob = await response.blob()\n const mimeType =\n part.source.mimeType || blob.type || inferMimeFromUrl(part.source.value)\n return new File([blob], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n}\n\nfunction inferMimeFromUrl(url: string): string {\n const match = url.match(/\\.(png|jpe?g|webp|gif)(?:\\?|#|$)/i)\n if (!match || !match[1]) return DEFAULT_MIME\n const ext = match[1].toLowerCase()\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n return `image/${ext}`\n}\n"],"mappings":";;;AAIA,IAAM,eAAe;AACrB,IAAM,cAAsC;CAC1C,aAAa;CACb,cAAc;CACd,aAAa;CACb,cAAc;CACd,aAAa;AACf;AAEA,SAAS,WAAW,UAA0B;CAC5C,OAAO,YAAY,aAAa,SAAS,MAAM,GAAG,CAAC,CAAC,MAAM;AAC5D;AAEA,SAAS,oBAA0B;CACjC,IAAI,OAAO,SAAS,aAClB,MAAM,IAAI,MACR,mHAEF;AAEJ;;;;;;;AAQA,eAAsB,gBACpB,MACA,cACA,eACA,aACe;CACf,kBAAkB;CAElB,IAAI,aAAa,KAAK,MAAM,GAG1B,MAAM,2BACJ,WACA,+GACF;CAGF,IAAI,KAAK,OAAO,SAAS,QAAQ;EAC/B,MAAM,WAAW,KAAK,OAAO,YAAY;EACzC,MAAM,QAAQ,oBAAoB,KAAK,OAAO,KAAK;EACnD,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,GAAG,aAAa,GAAG,WAAW,QAAQ,KAAK,EAClE,MAAM,SACR,CAAC;CACH;CAEA,IAAI,gBAAgB,KAAK,KAAK,OAAO,KAAK,KAAK,CAAC,eAC9C,MAAM,IAAI,MACR,sUAIsD,KAAK,OAAO,OACpE;CAGF,MAAM,WAAW,MAAM,MACrB,KAAK,OAAO,OACZ,cAAc,EAAE,QAAQ,YAAY,IAAI,KAAA,CAC1C;CACA,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,gCAAgC,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,KAAK,OAAO,OAC1F;CAEF,MAAM,OAAO,MAAM,SAAS,KAAK;CACjC,MAAM,WACJ,KAAK,OAAO,YAAY,KAAK,QAAQ,iBAAiB,KAAK,OAAO,KAAK;CACzE,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,GAAG,aAAa,GAAG,WAAW,QAAQ,KAAK,EACjE,MAAM,SACR,CAAC;AACH;AAEA,SAAS,iBAAiB,KAAqB;CAC7C,MAAM,QAAQ,IAAI,MAAM,mCAAmC;CAC3D,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,OAAO;CAChC,MAAM,MAAM,MAAM,EAAE,CAAC,YAAY;CACjC,IAAI,QAAQ,SAAS,QAAQ,QAAQ,OAAO;CAC5C,OAAO,SAAS;AAClB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-lovable",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"description": "Lovable AI Gateway adapter for TanStack AI chat, embeddings, image, video, speech, and transcription.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@vitest/coverage-v8": "4.1.10",
|
|
56
56
|
"vite": "^8.2.1",
|
|
57
|
-
"@tanstack/ai": "0.
|
|
57
|
+
"@tanstack/ai": "0.61.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@tanstack/ai": "^0.
|
|
60
|
+
"@tanstack/ai": "^0.61.0"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"openai": "^6.41.0",
|
|
64
64
|
"@tanstack/ai-utils": "^0.4.1",
|
|
65
|
-
"@tanstack/openai-base": "^0.
|
|
65
|
+
"@tanstack/openai-base": "^0.11.1"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "vite build",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { base64ToArrayBuffer } from '@tanstack/ai-utils'
|
|
2
|
+
import { isFileSource, unsupportedFileSourceError } from '@tanstack/ai'
|
|
2
3
|
import type { ImagePart, MediaInputMetadata } from '@tanstack/ai'
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MIME = 'image/png'
|
|
@@ -37,6 +38,15 @@ export async function imagePartToFile(
|
|
|
37
38
|
): Promise<File> {
|
|
38
39
|
ensureFileSupport()
|
|
39
40
|
|
|
41
|
+
if (isFileSource(part.source)) {
|
|
42
|
+
// These endpoints are multipart and require the actual bytes; there is no
|
|
43
|
+
// "reference an uploaded handle" option, and lovable has no Files API.
|
|
44
|
+
throw unsupportedFileSourceError(
|
|
45
|
+
'lovable',
|
|
46
|
+
'on the edit / input_reference endpoints, which require uploaded bytes — pass a data: URI or inline image data',
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
if (part.source.type === 'data') {
|
|
41
51
|
const mimeType = part.source.mimeType || DEFAULT_MIME
|
|
42
52
|
const bytes = base64ToArrayBuffer(part.source.value)
|