@umituz/react-native-ai-fal-provider 2.1.3 → 2.1.5

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.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/src/domain/constants/default-models.constants.ts +13 -4
  3. package/src/domain/types/model-selection.types.ts +9 -5
  4. package/src/exports/infrastructure.ts +2 -9
  5. package/src/infrastructure/services/fal-models.service.ts +8 -7
  6. package/src/infrastructure/services/fal-provider.ts +9 -8
  7. package/src/infrastructure/services/fal-queue-operations.ts +8 -1
  8. package/src/infrastructure/services/nsfw-content-error.ts +1 -1
  9. package/src/infrastructure/services/request-store.ts +78 -22
  10. package/src/infrastructure/utils/collections/array-filters.util.ts +12 -1
  11. package/src/infrastructure/utils/collections/array-sorters.util.ts +34 -0
  12. package/src/infrastructure/utils/collections/index.ts +0 -1
  13. package/src/infrastructure/utils/cost-tracker.ts +3 -1
  14. package/src/infrastructure/utils/cost-tracking-executor.util.ts +14 -3
  15. package/src/infrastructure/utils/date-format.util.ts +13 -47
  16. package/src/infrastructure/utils/fal-storage.util.ts +40 -1
  17. package/src/infrastructure/utils/formatting.util.ts +3 -21
  18. package/src/infrastructure/utils/helpers/index.ts +0 -1
  19. package/src/infrastructure/utils/helpers/timing-helpers.util.ts +1 -79
  20. package/src/infrastructure/utils/image-helpers.util.ts +9 -5
  21. package/src/infrastructure/utils/index.ts +11 -50
  22. package/src/infrastructure/utils/input-preprocessor.util.ts +53 -6
  23. package/src/infrastructure/utils/input-validator.util.ts +71 -28
  24. package/src/infrastructure/utils/job-metadata/job-metadata-format.util.ts +26 -1
  25. package/src/infrastructure/utils/number-format.util.ts +27 -20
  26. package/src/infrastructure/utils/parsers/index.ts +0 -1
  27. package/src/infrastructure/utils/parsers/json-parsers.util.ts +19 -3
  28. package/src/infrastructure/utils/string-format.util.ts +0 -59
  29. package/src/infrastructure/utils/type-guards/validation-guards.util.ts +12 -0
  30. package/src/presentation/hooks/use-models.ts +12 -4
  31. package/src/infrastructure/utils/collections/array-reducers.util.ts +0 -67
  32. package/src/infrastructure/utils/helpers/function-helpers.util.ts +0 -25
  33. package/src/infrastructure/utils/parsers/number-helpers.util.ts +0 -19
@@ -12,7 +12,12 @@ export function safeJsonParse<T = unknown>(
12
12
  ): T {
13
13
  try {
14
14
  return JSON.parse(data) as T;
15
- } catch {
15
+ } catch (error) {
16
+ console.warn(
17
+ '[json-parsers] Failed to parse JSON, using fallback:',
18
+ error instanceof Error ? error.message : String(error),
19
+ { dataPreview: data.substring(0, 100) }
20
+ );
16
21
  return fallback;
17
22
  }
18
23
  }
@@ -23,7 +28,12 @@ export function safeJsonParse<T = unknown>(
23
28
  export function safeJsonParseOrNull<T = unknown>(data: string): T | null {
24
29
  try {
25
30
  return JSON.parse(data) as T;
26
- } catch {
31
+ } catch (error) {
32
+ console.warn(
33
+ '[json-parsers] Failed to parse JSON, returning null:',
34
+ error instanceof Error ? error.message : String(error),
35
+ { dataPreview: data.substring(0, 100) }
36
+ );
27
37
  return null;
28
38
  }
29
39
  }
@@ -37,7 +47,12 @@ export function safeJsonStringify(
37
47
  ): string {
38
48
  try {
39
49
  return JSON.stringify(data);
40
- } catch {
50
+ } catch (error) {
51
+ console.warn(
52
+ '[json-parsers] Failed to stringify object, using fallback:',
53
+ error instanceof Error ? error.message : String(error),
54
+ { dataType: typeof data }
55
+ );
41
56
  return fallback;
42
57
  }
43
58
  }
@@ -50,6 +65,7 @@ export function isValidJson(data: string): boolean {
50
65
  JSON.parse(data);
51
66
  return true;
52
67
  } catch {
68
+ // Don't log here - this is expected to fail for validation checks
53
69
  return false;
54
70
  }
55
71
  }
@@ -12,62 +12,3 @@ export function truncateText(text: string, maxLength: number): string {
12
12
  }
13
13
  return text.slice(0, maxLength - 3) + "...";
14
14
  }
15
-
16
- /**
17
- * Capitalize first letter of string
18
- */
19
- export function capitalize(text: string): string {
20
- if (!text) return text;
21
- return text.charAt(0).toUpperCase() + text.slice(1);
22
- }
23
-
24
- /**
25
- * Convert string to title case
26
- */
27
- export function toTitleCase(text: string): string {
28
- return text
29
- .toLowerCase()
30
- .split(" ")
31
- .map((word) => capitalize(word))
32
- .join(" ");
33
- }
34
-
35
- /**
36
- * Convert string to slug
37
- */
38
- export function toSlug(text: string): string {
39
- return text
40
- .toLowerCase()
41
- .trim()
42
- .replace(/[^\w\s-]/g, "")
43
- .replace(/[\s_-]+/g, "-")
44
- .replace(/^-+|-+$/g, "");
45
- }
46
-
47
- /**
48
- * Format list of items with conjunction
49
- */
50
- export function formatList(items: readonly string[], conjunction: string = "and"): string {
51
- if (items.length === 0) return "";
52
- if (items.length === 1) return items[0] ?? "";
53
- if (items.length === 2) return items.join(` ${conjunction} `);
54
-
55
- const allButLast = items.slice(0, -1);
56
- const last = items[items.length - 1];
57
- return `${allButLast.join(", ")}, ${conjunction} ${last}`;
58
- }
59
-
60
- /**
61
- * Pluralize word based on count
62
- */
63
- export function pluralize(word: string, count: number): string {
64
- if (count === 1) return word;
65
- return `${word}s`;
66
- }
67
-
68
- /**
69
- * Format count with plural word
70
- */
71
- export function formatCount(word: string, count: number): string {
72
- return `${count} ${pluralize(word, count)}`;
73
- }
@@ -40,6 +40,8 @@ export function isValidApiKey(value: unknown): boolean {
40
40
 
41
41
  /**
42
42
  * Validate model ID format
43
+ * Pattern: org/model or org/model/version
44
+ * Allows dots for versions (e.g., v1.5) but prevents path traversal (..)
43
45
  */
44
46
  const MODEL_ID_PATTERN = /^[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_.]+(\/[a-zA-Z0-9-_.]+)?$/;
45
47
 
@@ -48,6 +50,16 @@ export function isValidModelId(value: unknown): boolean {
48
50
  return false;
49
51
  }
50
52
 
53
+ // Prevent path traversal attacks
54
+ if (value.includes('..')) {
55
+ return false;
56
+ }
57
+
58
+ // Ensure it doesn't start or end with dots
59
+ if (value.startsWith('.') || value.endsWith('.')) {
60
+ return false;
61
+ }
62
+
51
63
  return MODEL_ID_PATTERN.test(value) && value.length >= MIN_MODEL_ID_LENGTH;
52
64
  }
53
65
 
@@ -35,13 +35,21 @@ export function useModels(props: UseModelsProps): UseModelsReturn {
35
35
  const [isLoading, setIsLoading] = useState(true);
36
36
  const [error, setError] = useState<string | null>(null);
37
37
 
38
+ // Memoize config to prevent unnecessary re-renders when parent re-renders
39
+ // Only recreate when actual config values change
40
+ const memoizedConfig = useMemo(() => config, [
41
+ config?.initialModelId,
42
+ config?.defaultCreditCost,
43
+ config?.defaultModelId,
44
+ ]);
45
+
38
46
  // Direct effect - no intermediate callback needed
39
47
  useEffect(() => {
40
48
  setIsLoading(true);
41
49
  setError(null);
42
50
 
43
51
  try {
44
- const selectionData = falModelsService.getModelSelectionData(type, config);
52
+ const selectionData = falModelsService.getModelSelectionData(type, memoizedConfig);
45
53
  setModels(selectionData.models);
46
54
  setSelectedModel(selectionData.selectedModel);
47
55
  } catch (err) {
@@ -49,7 +57,7 @@ export function useModels(props: UseModelsProps): UseModelsReturn {
49
57
  } finally {
50
58
  setIsLoading(false);
51
59
  }
52
- }, [type, config]); // Direct dependencies
60
+ }, [type, memoizedConfig]);
53
61
 
54
62
  // Separate refresh callback for manual reloads
55
63
  const loadModels = useCallback(() => {
@@ -57,7 +65,7 @@ export function useModels(props: UseModelsProps): UseModelsReturn {
57
65
  setError(null);
58
66
 
59
67
  try {
60
- const selectionData = falModelsService.getModelSelectionData(type, config);
68
+ const selectionData = falModelsService.getModelSelectionData(type, memoizedConfig);
61
69
  setModels(selectionData.models);
62
70
  setSelectedModel(selectionData.selectedModel);
63
71
  } catch (err) {
@@ -65,7 +73,7 @@ export function useModels(props: UseModelsProps): UseModelsReturn {
65
73
  } finally {
66
74
  setIsLoading(false);
67
75
  }
68
- }, [type, config]);
76
+ }, [type, memoizedConfig]);
69
77
 
70
78
  const selectModel = useCallback(
71
79
  (modelId: string) => {
@@ -1,67 +0,0 @@
1
- /**
2
- * Array Reducer Utilities
3
- * Reduce, group, chunk, and aggregation operations
4
- */
5
-
6
- /**
7
- * Reduce array to sum of number property
8
- */
9
- export function sumByProperty<T>(
10
- items: readonly T[],
11
- numberProperty: keyof T
12
- ): number {
13
- return items.reduce((sum, item) => {
14
- const value = item[numberProperty] as unknown as number;
15
- return sum + (typeof value === "number" ? value : 0);
16
- }, 0);
17
- }
18
-
19
- /**
20
- * Group array by property value
21
- */
22
- export function groupByProperty<T>(
23
- items: readonly T[],
24
- property: keyof T
25
- ): Map<unknown, T[]> {
26
- const groups = new Map<unknown, T[]>();
27
- for (const item of items) {
28
- const key = item[property];
29
- const existing = groups.get(key);
30
- if (existing) {
31
- existing.push(item);
32
- } else {
33
- groups.set(key, [item]);
34
- }
35
- }
36
- return groups;
37
- }
38
-
39
- /**
40
- * Chunk array into smaller arrays of specified size
41
- */
42
- export function chunkArray<T>(items: readonly T[], chunkSize: number): T[][] {
43
- const result: T[][] = [];
44
- for (let i = 0; i < items.length; i += chunkSize) {
45
- result.push([...items.slice(i, i + chunkSize)]);
46
- }
47
- return result;
48
- }
49
-
50
- /**
51
- * Get distinct values of a property from array
52
- */
53
- export function distinctByProperty<T>(
54
- items: readonly T[],
55
- property: keyof T
56
- ): unknown[] {
57
- const seen = new Set<unknown>();
58
- const result: unknown[] = [];
59
- for (const item of items) {
60
- const value = item[property];
61
- if (!seen.has(value)) {
62
- seen.add(value);
63
- result.push(value);
64
- }
65
- }
66
- return result;
67
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Function Helper Utilities
3
- * Common functional programming helpers
4
- */
5
-
6
- /**
7
- * No-op function
8
- */
9
- export function noop(): void {
10
- // Intentionally empty
11
- }
12
-
13
- /**
14
- * Identity function
15
- */
16
- export function identity<T>(value: T): T {
17
- return value;
18
- }
19
-
20
- /**
21
- * Constant function (returns same value regardless of input)
22
- */
23
- export function constant<T>(value: T): () => T {
24
- return () => value;
25
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * Number Helper Utilities
3
- * Number manipulation and formatting
4
- */
5
-
6
- /**
7
- * Clamp number between min and max
8
- */
9
- export function clampNumber(value: number, min: number, max: number): number {
10
- return Math.min(Math.max(value, min), max);
11
- }
12
-
13
- /**
14
- * Round to decimal places
15
- */
16
- export function roundToDecimals(value: number, decimals: number): number {
17
- const multiplier = Math.pow(10, decimals);
18
- return Math.round(value * multiplier) / multiplier;
19
- }