@umituz/react-native-ai-fal-provider 1.1.4 → 1.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-fal-provider",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "FAL AI provider for React Native - implements IAIProvider interface for unified AI generation",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -90,6 +90,8 @@ export {
90
90
  extractBase64,
91
91
  getDataUriExtension,
92
92
  isImageDataUri,
93
+ uploadToFalStorage,
94
+ uploadMultipleToFalStorage,
93
95
  calculateTimeoutWithJitter,
94
96
  formatCreditCost,
95
97
  truncatePrompt,
@@ -28,6 +28,7 @@ import {
28
28
  buildImageFeatureInput as buildImageFeatureInputImpl,
29
29
  buildVideoFeatureInput as buildVideoFeatureInputImpl,
30
30
  } from "./fal-feature-models";
31
+ import { preprocessInput } from "../utils/input-preprocessor.util";
31
32
 
32
33
  declare const __DEV__: boolean | undefined;
33
34
 
@@ -114,7 +115,9 @@ export class FalProvider implements IAIProvider {
114
115
  options?: SubscribeOptions<T>,
115
116
  ): Promise<T> {
116
117
  this.validateInit();
117
- const key = createRequestKey(model, input);
118
+
119
+ const processedInput = await preprocessInput(input);
120
+ const key = createRequestKey(model, processedInput);
118
121
 
119
122
  const existing = getExistingRequest<T>(key);
120
123
  if (existing) {
@@ -127,7 +130,7 @@ export class FalProvider implements IAIProvider {
127
130
  const abortController = new AbortController();
128
131
  const operationId = this.costTracker?.startOperation(model, "subscribe");
129
132
 
130
- const promise = handleFalSubscription<T>(model, input, options, abortController.signal)
133
+ const promise = handleFalSubscription<T>(model, processedInput, options, abortController.signal)
131
134
  .then(({ result, requestId }) => {
132
135
  if (operationId && this.costTracker) {
133
136
  this.costTracker.completeOperation(operationId, model, "subscribe", requestId ?? undefined);
@@ -142,8 +145,9 @@ export class FalProvider implements IAIProvider {
142
145
 
143
146
  async run<T = unknown>(model: string, input: Record<string, unknown>, options?: RunOptions): Promise<T> {
144
147
  this.validateInit();
148
+ const processedInput = await preprocessInput(input);
145
149
  const operationId = this.costTracker?.startOperation(model, "run");
146
- const result = await handleFalRun<T>(model, input, options);
150
+ const result = await handleFalRun<T>(model, processedInput, options);
147
151
  if (operationId && this.costTracker) {
148
152
  this.costTracker.completeOperation(operationId, model, "run");
149
153
  }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * FAL Storage Utility
3
+ * Handles image uploads to FAL storage (React Native compatible)
4
+ */
5
+
6
+ import { fal } from "@fal-ai/client";
7
+ import {
8
+ base64ToTempFile,
9
+ deleteTempFile,
10
+ getFileSize,
11
+ detectMimeType,
12
+ } from "@umituz/react-native-design-system/filesystem";
13
+
14
+ declare const __DEV__: boolean | undefined;
15
+
16
+ /**
17
+ * Upload base64 image to FAL storage
18
+ * Uses design system's filesystem utilities for React Native compatibility
19
+ */
20
+ export async function uploadToFalStorage(base64: string): Promise<string> {
21
+ const tempUri = await base64ToTempFile(base64);
22
+ const fileSize = getFileSize(tempUri);
23
+ const mimeType = detectMimeType(base64);
24
+
25
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
26
+ console.log("[FalStorage] Uploading image", {
27
+ size: `${(fileSize / 1024).toFixed(1)}KB`,
28
+ type: mimeType,
29
+ });
30
+ }
31
+
32
+ const url = await fal.storage.upload(tempUri);
33
+
34
+ await deleteTempFile(tempUri);
35
+
36
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
37
+ console.log("[FalStorage] Upload complete", { url: url.slice(0, 60) + "..." });
38
+ }
39
+
40
+ return url;
41
+ }
42
+
43
+ /**
44
+ * Upload multiple images to FAL storage in parallel
45
+ */
46
+ export async function uploadMultipleToFalStorage(
47
+ images: string[],
48
+ ): Promise<string[]> {
49
+ return Promise.all(images.map(uploadToFalStorage));
50
+ }
@@ -10,6 +10,11 @@ export {
10
10
  isImageDataUri,
11
11
  } from "./image-helpers.util";
12
12
 
13
+ export {
14
+ uploadToFalStorage,
15
+ uploadMultipleToFalStorage,
16
+ } from "./fal-storage.util";
17
+
13
18
  export {
14
19
  truncatePrompt,
15
20
  sanitizePrompt,
@@ -40,6 +40,8 @@ export {
40
40
  extractBase64,
41
41
  getDataUriExtension,
42
42
  isImageDataUri,
43
+ uploadToFalStorage,
44
+ uploadMultipleToFalStorage,
43
45
  calculateTimeoutWithJitter,
44
46
  formatCreditCost,
45
47
  truncatePrompt,
@@ -84,3 +86,5 @@ export {
84
86
  export type { IJobStorage, InMemoryJobStorage } from "./job-storage";
85
87
 
86
88
  export { CostTracker } from "./cost-tracker";
89
+
90
+ export { preprocessInput } from "./input-preprocessor.util";
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Input Preprocessor Utility
3
+ * Detects and uploads base64 images to FAL storage before API calls
4
+ */
5
+
6
+ import { uploadToFalStorage } from "./fal-storage.util";
7
+
8
+ declare const __DEV__: boolean | undefined;
9
+
10
+ const IMAGE_URL_KEYS = [
11
+ "image_url",
12
+ "driver_image_url",
13
+ "base_image_url",
14
+ "swap_image_url",
15
+ "second_image_url",
16
+ "mask_url",
17
+ "input_image_url",
18
+ ];
19
+
20
+ function isBase64DataUri(value: unknown): value is string {
21
+ return typeof value === "string" && value.startsWith("data:image/");
22
+ }
23
+
24
+ /**
25
+ * Preprocess input by uploading base64 images to FAL storage
26
+ * Returns input with URLs instead of base64 data URIs
27
+ */
28
+ export async function preprocessInput(
29
+ input: Record<string, unknown>,
30
+ ): Promise<Record<string, unknown>> {
31
+ const result = { ...input };
32
+ const uploadPromises: Promise<void>[] = [];
33
+
34
+ for (const key of IMAGE_URL_KEYS) {
35
+ const value = result[key];
36
+ if (isBase64DataUri(value)) {
37
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
38
+ console.log(`[FalPreprocessor] Uploading ${key} to storage...`);
39
+ }
40
+
41
+ const uploadPromise = uploadToFalStorage(value).then((url) => {
42
+ result[key] = url;
43
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
44
+ console.log(`[FalPreprocessor] ${key} uploaded`, {
45
+ url: url.slice(0, 50) + "...",
46
+ });
47
+ }
48
+ });
49
+
50
+ uploadPromises.push(uploadPromise);
51
+ }
52
+ }
53
+
54
+ if (uploadPromises.length > 0) {
55
+ await Promise.all(uploadPromises);
56
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
57
+ console.log(`[FalPreprocessor] All images uploaded (${uploadPromises.length})`);
58
+ }
59
+ }
60
+
61
+ return result;
62
+ }