@umituz/react-native-ai-generation-content 1.68.0 → 1.70.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.68.0",
3
+ "version": "1.70.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",
@@ -11,7 +11,7 @@ import { QUEUE_STATUS, CREATION_STATUS } from "../../../../domain/constants/queu
11
11
  import { DEFAULT_POLL_INTERVAL_MS } from "../../../../infrastructure/constants/polling.constants";
12
12
  import {
13
13
  extractResultUrl,
14
- type FalResult,
14
+ type GenerationResult,
15
15
  } from "../../../generation/wizard/presentation/hooks/generation-result.utils";
16
16
  import type { Creation } from "../../domain/entities/Creation";
17
17
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
@@ -54,12 +54,15 @@ export function useProcessingJobsPoller(
54
54
 
55
55
  pollJobRef.current = async (creation: Creation) => {
56
56
  if (!userId || !creation.requestId || !creation.model) return;
57
+
57
58
  if (pollingRef.current.has(creation.id)) return;
59
+ pollingRef.current.add(creation.id);
58
60
 
59
61
  const provider = providerRegistry.getActiveProvider();
60
- if (!provider || !provider.isInitialized()) return;
61
-
62
- pollingRef.current.add(creation.id);
62
+ if (!provider || !provider.isInitialized()) {
63
+ pollingRef.current.delete(creation.id);
64
+ return;
65
+ }
63
66
 
64
67
  try {
65
68
  if (typeof __DEV__ !== "undefined" && __DEV__) {
@@ -73,7 +76,7 @@ export function useProcessingJobsPoller(
73
76
  }
74
77
 
75
78
  if (status.status === QUEUE_STATUS.COMPLETED) {
76
- const result = await provider.getJobResult<FalResult>(creation.model, creation.requestId);
79
+ const result = await provider.getJobResult<GenerationResult>(creation.model, creation.requestId);
77
80
  const urls = extractResultUrl(result);
78
81
  if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[ProcessingJobsPoller] Completed:", creation.id, urls);
79
82
 
@@ -8,6 +8,7 @@ import { StepType } from "../../../../../domain/entities/flow-config.types";
8
8
  import type { StepDefinition } from "../../../../../domain/entities/flow-config.types";
9
9
  import type { WizardStepConfig } from "../../domain/entities/wizard-step.types";
10
10
  import type { WizardFeatureConfig, ScenarioBasedConfig } from "../../domain/entities/wizard-feature.types";
11
+ import { buildWizardConfigFromScenario } from "../../domain/entities/wizard-feature.types";
11
12
 
12
13
  /**
13
14
  * Convert wizard step config to flow step definition
@@ -104,7 +105,6 @@ export const quickBuildWizard = (
104
105
  featureId: string,
105
106
  scenarioConfig: ScenarioBasedConfig,
106
107
  ): StepDefinition[] => {
107
- const { buildWizardConfigFromScenario } = require("../../domain/entities/wizard-feature.types");
108
108
  const wizardConfig = buildWizardConfigFromScenario(featureId, scenarioConfig);
109
109
  return buildFlowStepsFromWizard(wizardConfig, {
110
110
  includePreview: true,
@@ -1,21 +1,21 @@
1
1
  /**
2
2
  * Generation Result Utilities
3
- * Shared utilities for extracting and processing generation results
3
+ * Provider-agnostic utilities for extracting generation results
4
4
  */
5
5
 
6
- export interface FalErrorDetail {
6
+ export interface GenerationErrorDetail {
7
7
  msg?: string;
8
8
  type?: string;
9
9
  loc?: string[];
10
10
  input?: string;
11
11
  }
12
12
 
13
- export interface FalResult {
13
+ export interface GenerationResult {
14
14
  video?: { url?: string };
15
15
  output?: string;
16
16
  images?: Array<{ url?: string }>;
17
17
  image?: { url?: string };
18
- detail?: FalErrorDetail[];
18
+ detail?: GenerationErrorDetail[];
19
19
  error?: string;
20
20
  }
21
21
 
@@ -27,8 +27,7 @@ export interface GenerationUrls {
27
27
  /**
28
28
  * Check if result contains an error and throw with appropriate message
29
29
  */
30
- function checkForErrors(result: FalResult): void {
31
- // Check for FAL API error format: {detail: [{msg, type}]}
30
+ function checkForErrors(result: GenerationResult): void {
32
31
  if (result.detail && Array.isArray(result.detail) && result.detail.length > 0) {
33
32
  const firstError = result.detail[0];
34
33
  if (!firstError) return;
@@ -36,7 +35,6 @@ function checkForErrors(result: FalResult): void {
36
35
  const errorType = firstError.type || "unknown";
37
36
  const errorMsg = firstError.msg || "Generation failed";
38
37
 
39
- // Map error type to translation key
40
38
  if (errorType === "content_policy_violation") {
41
39
  throw new Error("error.generation.content_policy");
42
40
  }
@@ -48,27 +46,23 @@ function checkForErrors(result: FalResult): void {
48
46
  throw new Error(errorMsg);
49
47
  }
50
48
 
51
- // Check for simple error field
52
49
  if (result.error && typeof result.error === "string" && result.error.length > 0) {
53
50
  throw new Error(result.error);
54
51
  }
55
52
  }
56
53
 
57
54
  /**
58
- * Extracts image/video URL from FAL result
59
- * Handles various result formats from different FAL models
55
+ * Extracts image/video URL from generation result
56
+ * Handles various result formats from different providers
60
57
  * Throws error if result contains error information
61
58
  */
62
- export function extractResultUrl(result: FalResult): GenerationUrls {
63
- // First check for errors in the result
59
+ export function extractResultUrl(result: GenerationResult): GenerationUrls {
64
60
  checkForErrors(result);
65
61
 
66
- // Video result
67
62
  if (result.video?.url && typeof result.video.url === "string") {
68
63
  return { videoUrl: result.video.url };
69
64
  }
70
65
 
71
- // Output URL (some models return direct URL)
72
66
  if (typeof result.output === "string" && result.output.length > 0 && result.output.startsWith("http")) {
73
67
  if (result.output.includes(".mp4") || result.output.includes("video")) {
74
68
  return { videoUrl: result.output };
@@ -76,7 +70,6 @@ export function extractResultUrl(result: FalResult): GenerationUrls {
76
70
  return { imageUrl: result.output };
77
71
  }
78
72
 
79
- // Images array (most image models) with bounds checking
80
73
  if (result.images && Array.isArray(result.images) && result.images.length > 0) {
81
74
  const firstImage = result.images[0];
82
75
  if (firstImage?.url && typeof firstImage.url === "string") {
@@ -84,7 +77,6 @@ export function extractResultUrl(result: FalResult): GenerationUrls {
84
77
  }
85
78
  }
86
79
 
87
- // Single image
88
80
  if (result.image?.url && typeof result.image.url === "string") {
89
81
  return { imageUrl: result.image.url };
90
82
  }
@@ -27,9 +27,12 @@ export function useGenerationPhase(options?: UseGenerationPhaseOptions): Generat
27
27
  const { queuedDuration = 5000 } = options ?? {};
28
28
 
29
29
  const [phase, setPhase] = useState<GenerationPhase>("queued");
30
- const startTimeRef = useRef(Date.now());
30
+ const startTimeRef = useRef<number>(Date.now());
31
31
 
32
32
  useEffect(() => {
33
+ startTimeRef.current = Date.now();
34
+ setPhase("queued");
35
+
33
36
  const interval = setInterval(() => {
34
37
  const elapsed = Date.now() - startTimeRef.current;
35
38
 
@@ -1,5 +1,5 @@
1
1
  import { providerRegistry } from "../../../../../infrastructure/services/provider-registry.service";
2
- import { extractResultUrl, type FalResult, type GenerationUrls } from "./generation-result.utils";
2
+ import { extractResultUrl, type GenerationUrls } from "./generation-result.utils";
3
3
  import { QUEUE_STATUS } from "../../../../../domain/constants/queue-status.constants";
4
4
 
5
5
  declare const __DEV__: boolean;
@@ -34,7 +34,7 @@ export const pollQueueStatus = async (params: PollParams): Promise<void> => {
34
34
 
35
35
  if (status.status === QUEUE_STATUS.COMPLETED) {
36
36
  try {
37
- const result = await provider.getJobResult<FalResult>(model, requestId);
37
+ const result = await provider.getJobResult(model, requestId);
38
38
  await onComplete(extractResultUrl(result));
39
39
  } catch (resultErr) {
40
40
  const errorMessage = resultErr instanceof Error ? resultErr.message : "Generation failed";
@@ -4,7 +4,6 @@
4
4
 
5
5
  import { BaseExecutor } from "../../../../infrastructure/executors/base-executor";
6
6
  import { isSuccess, type Result } from "../../../../domain/types/result.types";
7
- import { checkFalApiError } from "../../../../infrastructure/utils";
8
7
  import {
9
8
  defaultExtractVideoResult,
10
9
  type ExtractedVideoResult,
@@ -55,7 +54,6 @@ class ImageToVideoExecutor extends BaseExecutor<
55
54
  });
56
55
 
57
56
  this.log("info", `Complete, keys: ${result ? Object.keys(result as object) : "null"}`);
58
- checkFalApiError(result);
59
57
  return result;
60
58
  }
61
59
 
@@ -7,74 +7,34 @@ export class PromptHistoryRepository implements IPromptHistoryRepository {
7
7
  private readonly maxStorageSize = 100;
8
8
 
9
9
  save(prompt: GeneratedPrompt): Promise<AIPromptResult<void>> {
10
- try {
11
- this.storage.push(prompt);
12
- this.trimStorage();
13
- return Promise.resolve({ success: true, data: undefined });
14
- } catch {
15
- return Promise.resolve({
16
- success: false,
17
- error: 'STORAGE_ERROR',
18
- message: 'Failed to save prompt to history'
19
- });
20
- }
10
+ this.storage.push(prompt);
11
+ this.trimStorage();
12
+ return Promise.resolve({ success: true, data: undefined });
21
13
  }
22
14
 
23
15
  findRecent(limit: number = 50): Promise<AIPromptResult<GeneratedPrompt[]>> {
24
- try {
25
- const prompts = this.storage.slice(-limit);
26
- return Promise.resolve({ success: true, data: prompts });
27
- } catch {
28
- return Promise.resolve({
29
- success: false,
30
- error: 'STORAGE_ERROR',
31
- message: 'Failed to retrieve recent prompts'
32
- });
33
- }
16
+ const prompts = this.storage.slice(-limit);
17
+ return Promise.resolve({ success: true, data: prompts });
34
18
  }
35
19
 
36
20
  findByTemplateId(
37
21
  templateId: string,
38
22
  limit: number = 20
39
23
  ): Promise<AIPromptResult<GeneratedPrompt[]>> {
40
- try {
41
- const prompts = this.storage
42
- .filter(prompt => prompt.templateId === templateId)
43
- .slice(-limit);
44
- return Promise.resolve({ success: true, data: prompts });
45
- } catch {
46
- return Promise.resolve({
47
- success: false,
48
- error: 'STORAGE_ERROR',
49
- message: 'Failed to retrieve prompts by template ID'
50
- });
51
- }
24
+ const prompts = this.storage
25
+ .filter(prompt => prompt.templateId === templateId)
26
+ .slice(-limit);
27
+ return Promise.resolve({ success: true, data: prompts });
52
28
  }
53
29
 
54
30
  delete(id: string): Promise<AIPromptResult<void>> {
55
- try {
56
- this.storage = this.storage.filter(prompt => prompt.id !== id);
57
- return Promise.resolve({ success: true, data: undefined });
58
- } catch {
59
- return Promise.resolve({
60
- success: false,
61
- error: 'STORAGE_ERROR',
62
- message: 'Failed to delete prompt'
63
- });
64
- }
31
+ this.storage = this.storage.filter(prompt => prompt.id !== id);
32
+ return Promise.resolve({ success: true, data: undefined });
65
33
  }
66
34
 
67
35
  clear(): Promise<AIPromptResult<void>> {
68
- try {
69
- this.storage = [];
70
- return Promise.resolve({ success: true, data: undefined });
71
- } catch {
72
- return Promise.resolve({
73
- success: false,
74
- error: 'STORAGE_ERROR',
75
- message: 'Failed to clear prompt history'
76
- });
77
- }
36
+ this.storage = [];
37
+ return Promise.resolve({ success: true, data: undefined });
78
38
  }
79
39
 
80
40
  private trimStorage(): void {
@@ -56,5 +56,9 @@ export async function fetchWithTimeout<T>(
56
56
  return createErrorResponse(getErrorMessage(result.error));
57
57
  }
58
58
 
59
- return createSuccessResponse(result.data ?? null as T);
59
+ if (result.data === undefined) {
60
+ return createErrorResponse("No data received from server");
61
+ }
62
+
63
+ return createSuccessResponse(result.data);
60
64
  }
@@ -16,6 +16,11 @@ export function createTimeoutController(
16
16
  }
17
17
 
18
18
  const controller = new AbortController();
19
- setTimeout(() => controller.abort(), timeout);
19
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
20
+
21
+ controller.signal.addEventListener('abort', () => {
22
+ clearTimeout(timeoutId);
23
+ }, { once: true });
24
+
20
25
  return controller;
21
26
  }
@@ -4,7 +4,7 @@
4
4
  * Output: video URL
5
5
  */
6
6
 
7
- import { extractErrorMessage, checkFalApiError, validateProvider, prepareVideoInputData } from "../utils";
7
+ import { extractErrorMessage, validateProvider, prepareVideoInputData } from "../utils";
8
8
  import { extractVideoResult } from "../utils/url-extractor";
9
9
  import { DEFAULT_MAX_POLL_TIME_MS } from "../constants";
10
10
  import type { VideoFeatureType } from "../../domain/interfaces";
@@ -42,8 +42,6 @@ export async function executeVideoFeature(
42
42
  onQueueUpdate: (status) => onStatusChange?.(status.status),
43
43
  });
44
44
 
45
- checkFalApiError(result);
46
-
47
45
  const videoUrl = (extractResult ?? extractVideoResult)(result);
48
46
 
49
47
  if (!videoUrl) {
@@ -2,7 +2,21 @@
2
2
  * Error Factory Functions
3
3
  */
4
4
 
5
- import type { GenerationError, GenerationErrorTypeValue } from "./extraction-types";
5
+ export const GenerationErrorType = {
6
+ CONTENT_POLICY: "content_policy",
7
+ RATE_LIMIT: "rate_limit",
8
+ TIMEOUT: "timeout",
9
+ VALIDATION: "validation",
10
+ NETWORK: "network",
11
+ UNKNOWN: "unknown",
12
+ } as const;
13
+
14
+ export type GenerationErrorTypeValue = typeof GenerationErrorType[keyof typeof GenerationErrorType];
15
+
16
+ export interface GenerationError extends Error {
17
+ errorType: GenerationErrorTypeValue;
18
+ translationKey: string;
19
+ }
6
20
 
7
21
  /**
8
22
  * Create a structured generation error
@@ -4,8 +4,6 @@
4
4
 
5
5
  import { getErrorMessage } from "./error-extractors";
6
6
 
7
- declare const __DEV__: boolean;
8
-
9
7
  /**
10
8
  * Wraps an async function with error handling
11
9
  */
@@ -25,13 +25,13 @@ export async function retryWithBackoff<T>(
25
25
 
26
26
  let lastError: Error | undefined;
27
27
 
28
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
28
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
29
29
  try {
30
30
  return await operation();
31
31
  } catch (error) {
32
32
  lastError = error instanceof Error ? error : new Error(getErrorMessage(error));
33
33
 
34
- if (attempt === maxRetries || !shouldRetry(lastError)) {
34
+ if (!shouldRetry(lastError)) {
35
35
  throw lastError;
36
36
  }
37
37
 
@@ -40,5 +40,5 @@ export async function retryWithBackoff<T>(
40
40
  }
41
41
  }
42
42
 
43
- throw lastError!;
43
+ throw lastError ?? new Error("Operation failed after retries");
44
44
  }
@@ -9,8 +9,8 @@ export * from "./error-handlers";
9
9
  export * from "./error-factory";
10
10
  export * from "./error-types";
11
11
  export * from "./message-extractor";
12
- export * from "./fal-error-checker";
13
12
  export * from "./classifier-helpers";
13
+ export * from "./result-polling";
14
14
  export * from "./validation.util";
15
15
  export * from "../../domains/background/infrastructure/utils/polling-interval.util";
16
16
  export * from "./progress-calculator.util";
@@ -2,8 +2,7 @@
2
2
  * Error Message Extraction Functions
3
3
  */
4
4
 
5
- import { GenerationErrorType } from "./extraction-types";
6
- import { isGenerationError } from "./error-factory";
5
+ import { GenerationErrorType, isGenerationError } from "./error-factory";
7
6
 
8
7
  declare const __DEV__: boolean;
9
8
 
@@ -1,37 +0,0 @@
1
- /**
2
- * Error Extraction Type Definitions
3
- */
4
-
5
- /**
6
- * FAL API error detail item
7
- */
8
- export interface FalErrorDetail {
9
- readonly msg?: string;
10
- readonly type?: string;
11
- readonly loc?: string[];
12
- readonly input?: string;
13
- readonly url?: string;
14
- }
15
-
16
- /**
17
- * Error types for user-friendly messages
18
- */
19
- export const GenerationErrorType = {
20
- CONTENT_POLICY: "content_policy",
21
- VALIDATION: "validation",
22
- NETWORK: "network",
23
- TIMEOUT: "timeout",
24
- RATE_LIMIT: "rate_limit",
25
- QUOTA_EXCEEDED: "quota_exceeded",
26
- UNKNOWN: "unknown",
27
- } as const;
28
-
29
- export type GenerationErrorTypeValue = typeof GenerationErrorType[keyof typeof GenerationErrorType];
30
-
31
- /**
32
- * Structured generation error
33
- */
34
- export interface GenerationError extends Error {
35
- readonly errorType: GenerationErrorTypeValue;
36
- readonly translationKey: string;
37
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * FAL API Error Checker
3
- */
4
-
5
- import type { FalErrorDetail } from "./extraction-types";
6
- import { GenerationErrorType } from "./extraction-types";
7
- import { createGenerationError } from "./error-factory";
8
-
9
- declare const __DEV__: boolean;
10
-
11
- /**
12
- * Check if result contains a FAL API error response
13
- * FAL sometimes returns errors with COMPLETED status
14
- */
15
- export function checkFalApiError(result: unknown): void {
16
- if (!result || typeof result !== "object") return;
17
-
18
- const resultObj = result as { detail?: FalErrorDetail[] };
19
-
20
- // FAL API error format: {detail: [{msg, type, loc}]}
21
- if (Array.isArray(resultObj.detail) && resultObj.detail.length > 0) {
22
- const firstError = resultObj.detail[0];
23
- const errorType = firstError?.type || "unknown";
24
- const errorMsg = firstError?.msg || "Unknown API error";
25
-
26
- if (typeof __DEV__ !== "undefined" && __DEV__) {
27
- console.error("[FalApiError] Detected error in result:", {
28
- type: errorType,
29
- message: errorMsg,
30
- });
31
- }
32
-
33
- // Throw specific error based on type
34
- if (errorType === "content_policy_violation") {
35
- throw createGenerationError(GenerationErrorType.CONTENT_POLICY, errorMsg);
36
- }
37
-
38
- if (errorType === "validation_error" || errorType.includes("validation")) {
39
- throw createGenerationError(GenerationErrorType.VALIDATION, errorMsg);
40
- }
41
-
42
- throw createGenerationError(GenerationErrorType.UNKNOWN, errorMsg);
43
- }
44
- }