@umituz/react-native-ai-generation-content 1.77.0 → 1.78.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.77.0",
3
+ "version": "1.78.0",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -4,10 +4,57 @@
4
4
  */
5
5
 
6
6
  import { readFileAsBase64 } from "@umituz/react-native-design-system";
7
+ import { manipulateAsync, SaveFormat } from "expo-image-manipulator";
8
+ import { Image } from "react-native";
7
9
  import { PHOTO_KEY_PREFIX } from "../wizard-strategy.constants";
8
10
 
9
11
  declare const __DEV__: boolean;
10
12
 
13
+ const MIN_IMAGE_DIMENSION = 300;
14
+
15
+ /**
16
+ * Get image dimensions from URI
17
+ */
18
+ function getImageSize(uri: string): Promise<{ width: number; height: number }> {
19
+ return new Promise((resolve, reject) => {
20
+ Image.getSize(uri, (width, height) => resolve({ width, height }), reject);
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Ensure image meets minimum dimensions (300x300) required by AI providers.
26
+ * Returns the original URI if already large enough, or a resized URI.
27
+ */
28
+ async function ensureMinimumSize(uri: string): Promise<string> {
29
+ try {
30
+ const { width, height } = await getImageSize(uri);
31
+
32
+ if (width >= MIN_IMAGE_DIMENSION && height >= MIN_IMAGE_DIMENSION) {
33
+ return uri;
34
+ }
35
+
36
+ const scale = Math.max(MIN_IMAGE_DIMENSION / width, MIN_IMAGE_DIMENSION / height);
37
+ const newWidth = Math.ceil(width * scale);
38
+ const newHeight = Math.ceil(height * scale);
39
+
40
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
41
+ console.log("[PhotoExtraction] Resizing small image", {
42
+ from: `${width}x${height}`,
43
+ to: `${newWidth}x${newHeight}`,
44
+ });
45
+ }
46
+
47
+ const result = await manipulateAsync(uri, [{ resize: { width: newWidth, height: newHeight } }], {
48
+ format: SaveFormat.JPEG,
49
+ compress: 0.9,
50
+ });
51
+
52
+ return result.uri;
53
+ } catch {
54
+ return uri;
55
+ }
56
+ }
57
+
11
58
  /**
12
59
  * Extracts photo URIs from wizard data
13
60
  */
@@ -59,7 +106,8 @@ export async function extractPhotosAsBase64(
59
106
  const results = await Promise.allSettled(
60
107
  photoUris.map(async (uri, index) => {
61
108
  try {
62
- return await readFileAsBase64(uri);
109
+ const resizedUri = await ensureMinimumSize(uri);
110
+ return await readFileAsBase64(resizedUri);
63
111
  } catch (error) {
64
112
  if (typeof __DEV__ !== "undefined" && __DEV__) {
65
113
  console.error(`[PhotoExtraction] Failed to read photo ${index}:`, error);
@@ -7,6 +7,39 @@ declare const __DEV__: boolean;
7
7
  /** Max consecutive transient errors before aborting */
8
8
  const MAX_CONSECUTIVE_ERRORS = 5;
9
9
 
10
+ /**
11
+ * Extract meaningful error message from various error formats.
12
+ * Fal AI client throws ValidationError with empty .message but details in .body/.detail
13
+ */
14
+ function extractErrorMessage(err: unknown): string {
15
+ if (!err) return "Generation failed";
16
+
17
+ // Standard Error with message
18
+ if (err instanceof Error && err.message && err.message.length > 0) {
19
+ return err.message;
20
+ }
21
+
22
+ // Fal AI ValidationError - has .body.detail array
23
+ const errObj = err as Record<string, unknown>;
24
+ if (errObj.body && typeof errObj.body === "object") {
25
+ const body = errObj.body as Record<string, unknown>;
26
+ if (Array.isArray(body.detail) && body.detail.length > 0) {
27
+ const first = body.detail[0] as { msg?: string; type?: string } | undefined;
28
+ if (first?.msg) return first.msg;
29
+ }
30
+ }
31
+
32
+ // Direct .detail array on error object
33
+ if (Array.isArray(errObj.detail) && errObj.detail.length > 0) {
34
+ const first = errObj.detail[0] as { msg?: string } | undefined;
35
+ if (first?.msg) return first.msg;
36
+ }
37
+
38
+ // Fallback to string conversion
39
+ const str = String(err);
40
+ return str.length > 0 && str !== "[object Object]" ? str : "Generation failed";
41
+ }
42
+
10
43
  interface PollParams {
11
44
  requestId: string;
12
45
  model: string;
@@ -56,7 +89,7 @@ export const pollQueueStatus = async (params: PollParams): Promise<void> => {
56
89
  }
57
90
  await onComplete(urls);
58
91
  } catch (resultErr) {
59
- const errorMessage = resultErr instanceof Error ? resultErr.message : "Generation failed";
92
+ const errorMessage = extractErrorMessage(resultErr);
60
93
  if (__DEV__) {
61
94
  console.error("[VideoQueueGeneration] ❌ Result error:", errorMessage);
62
95
  console.error("[VideoQueueGeneration] ❌ Full error:", resultErr);