From 49ad058d47a6dd24aed18d859fe19158c09b143e Mon Sep 17 00:00:00 2001 From: shrimbly Date: Fri, 9 Jan 2026 22:44:03 +1300 Subject: [PATCH] feat(05-01): create uploadImageForUrl utility Co-Authored-By: Claude Opus 4.5 --- src/lib/images/index.ts | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/lib/images/index.ts diff --git a/src/lib/images/index.ts b/src/lib/images/index.ts new file mode 100644 index 00000000..912b4ce6 --- /dev/null +++ b/src/lib/images/index.ts @@ -0,0 +1,85 @@ +/** + * Image URL Utilities + * + * Functions for uploading images to the local server and generating + * URLs that external providers can fetch from. + * + * Usage: + * 1. Check if image should use URL: shouldUseImageUrl(base64DataUrl) + * 2. Upload and get URL: const { url, id } = uploadImageForUrl(base64, baseUrl) + * 3. Pass URL to provider + * 4. Clean up after provider fetches: deleteImage(id) + */ + +import { storeImage } from "./store"; + +// Re-export store functions for convenience +export { + storeImage, + getImage, + deleteImage, + deleteImages, + getStoreStats, +} from "./store"; + +/** + * Size threshold for using URL instead of base64 (256KB) + * Replicate recommends URLs for files larger than this + */ +const URL_SIZE_THRESHOLD_BYTES = 256 * 1024; + +/** + * Upload an image to the local store and return its URL + * + * @param base64DataUrl - Image as base64 data URL + * @param baseUrl - Server base URL (e.g., "http://localhost:3000") + * @returns Object containing full URL and image ID for cleanup + * + * @example + * const { url, id } = uploadImageForUrl(imageData, "http://localhost:3000"); + * // url: "http://localhost:3000/api/images/abc-123" + * // Use URL with provider, then clean up: + * deleteImage(id); + */ +export function uploadImageForUrl( + base64DataUrl: string, + baseUrl: string +): { url: string; id: string } { + const id = storeImage(base64DataUrl); + const url = `${baseUrl}/api/images/${id}`; + + return { url, id }; +} + +/** + * Check if an image should use URL-based transfer instead of base64 + * + * Returns true if the base64 data exceeds 256KB, which is Replicate's + * recommended threshold for using URLs instead of data URIs. + * + * @param base64DataUrl - Image as base64 data URL + * @returns true if image should use URL, false if base64 is fine + * + * @example + * if (shouldUseImageUrl(imageData)) { + * const { url, id } = uploadImageForUrl(imageData, baseUrl); + * // use URL + * } else { + * // use base64 directly + * } + */ +export function shouldUseImageUrl(base64DataUrl: string): boolean { + // Extract base64 data portion from data URL + const match = base64DataUrl.match(/^data:[^;]+;base64,(.+)$/); + if (!match) { + // If not a valid data URL, default to using URL for safety + return true; + } + + const base64Data = match[1]; + // Base64 encoded data is ~33% larger than raw binary + // Calculate approximate raw size: base64Length * 3/4 + const approximateByteSize = (base64Data.length * 3) / 4; + + return approximateByteSize > URL_SIZE_THRESHOLD_BYTES; +}