@umituz/react-native-ai-generation-content 1.17.308 → 1.17.310

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.17.308",
3
+ "version": "1.17.310",
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",
@@ -15,7 +15,7 @@ export type {
15
15
  } from "./domain/types";
16
16
  export { COUPLE_FUTURE_DEFAULTS } from "./domain/types";
17
17
  export { useCoupleFutureGeneration } from "./presentation/hooks/useCoupleFutureGeneration";
18
- export type { UseCoupleFutureGenerationConfig } from "./presentation/hooks/useCoupleFutureGeneration";
18
+ export type { CoupleFutureConfig as UseCoupleFutureGenerationConfig } from "./presentation/hooks/useCoupleFutureGeneration";
19
19
  export {
20
20
  RomanticMoodSelector,
21
21
  ArtStyleSelector,
@@ -1,46 +1,31 @@
1
1
  /**
2
2
  * useCoupleFutureGeneration Hook
3
- * Couple future generation with centralized credit management
3
+ * Couple future generation using centralized orchestrator
4
4
  */
5
5
 
6
- import { useCallback } from "react";
7
- import { usePhotoGeneration } from "../../../../presentation/hooks/usePhotoGeneration";
6
+ import { useMemo, useCallback } from "react";
7
+ import {
8
+ useGenerationOrchestrator,
9
+ type GenerationStrategy,
10
+ type AlertMessages,
11
+ } from "../../../../presentation/hooks/generation";
8
12
  import { executeCoupleFuture } from "../../infrastructure/executor";
9
13
  import type { CoupleFutureInput } from "../../domain/types";
10
- import type {
11
- PhotoGenerationConfig,
12
- PhotoGenerationError,
13
- } from "../../../../presentation/hooks/photo-generation.types";
14
14
  import { createCreationsRepository } from "../../../../domains/creations/infrastructure/adapters";
15
15
  import type { Creation } from "../../../../domains/creations/domain/entities/Creation";
16
16
 
17
- export interface UseCoupleFutureGenerationConfig<
18
- TInput extends CoupleFutureInput,
19
- TResult,
20
- > {
17
+ export interface CoupleFutureConfig<TResult> {
21
18
  userId: string | undefined;
22
- processResult: (
23
- imageUrl: string,
24
- input: TInput,
25
- ) => Promise<TResult> | TResult;
26
- buildCreation?: (result: TResult, input: TInput) => Creation | null;
19
+ processResult: (imageUrl: string, input: CoupleFutureInput) => TResult;
20
+ buildCreation?: (result: TResult, input: CoupleFutureInput) => Creation | null;
27
21
  onCreditsExhausted?: () => void;
28
22
  onSuccess?: (result: TResult) => void;
29
23
  onError?: (error: string) => void;
30
- alertMessages: {
31
- networkError: string;
32
- policyViolation: string;
33
- saveFailed: string;
34
- creditFailed: string;
35
- unknown: string;
36
- };
24
+ alertMessages: AlertMessages;
37
25
  }
38
26
 
39
- export const useCoupleFutureGeneration = <
40
- TInput extends CoupleFutureInput,
41
- TResult,
42
- >(
43
- config: UseCoupleFutureGenerationConfig<TInput, TResult>,
27
+ export const useCoupleFutureGeneration = <TResult>(
28
+ config: CoupleFutureConfig<TResult>,
44
29
  ) => {
45
30
  const {
46
31
  userId,
@@ -52,49 +37,54 @@ export const useCoupleFutureGeneration = <
52
37
  alertMessages,
53
38
  } = config;
54
39
 
55
- const repository = useCallback(
40
+ const repository = useMemo(
56
41
  () => createCreationsRepository("creations"),
57
42
  [],
58
43
  );
59
44
 
60
- const generationConfig: PhotoGenerationConfig<TInput, TResult, void> = {
61
- userId,
62
- creditCost: 1,
63
- onCreditsExhausted,
64
- generate: async (
65
- input: TInput,
66
- onProgress?: (progress: number) => void,
67
- ) => {
68
- const result = await executeCoupleFuture(
69
- {
70
- partnerABase64: input.partnerABase64,
71
- partnerBBase64: input.partnerBBase64,
72
- prompt: input.prompt,
73
- },
74
- { onProgress },
75
- );
45
+ const strategy: GenerationStrategy<CoupleFutureInput, TResult> = useMemo(
46
+ () => ({
47
+ execute: async (input, onProgress) => {
48
+ const result = await executeCoupleFuture(
49
+ {
50
+ partnerABase64: input.partnerABase64,
51
+ partnerBBase64: input.partnerBBase64,
52
+ prompt: input.prompt,
53
+ },
54
+ { onProgress },
55
+ );
76
56
 
77
- if (!result.success || !result.imageUrl) {
78
- throw new Error(result.error || "Generation failed");
79
- }
57
+ if (!result.success || !result.imageUrl) {
58
+ throw new Error(result.error || "Generation failed");
59
+ }
80
60
 
81
- return processResult(result.imageUrl, input);
82
- },
83
- save: async (result: TResult, input: TInput) => {
84
- if (!userId || !buildCreation) {
85
- return;
86
- }
87
- const creation = buildCreation(result, input);
88
- if (creation) {
89
- await repository().create(userId, creation);
90
- }
91
- },
92
- onSuccess,
93
- onError: (error: PhotoGenerationError) => {
61
+ return processResult(result.imageUrl, input);
62
+ },
63
+ getCreditCost: () => 1,
64
+ save: buildCreation
65
+ ? async (result, uid) => {
66
+ const creation = buildCreation(result, {} as CoupleFutureInput);
67
+ if (creation) {
68
+ await repository.create(uid, creation);
69
+ }
70
+ }
71
+ : undefined,
72
+ }),
73
+ [processResult, buildCreation, repository],
74
+ );
75
+
76
+ const handleError = useCallback(
77
+ (error: { message: string }) => {
94
78
  onError?.(error.message);
95
79
  },
96
- alertMessages,
97
- };
80
+ [onError],
81
+ );
98
82
 
99
- return usePhotoGeneration(generationConfig);
83
+ return useGenerationOrchestrator(strategy, {
84
+ userId,
85
+ alertMessages,
86
+ onCreditsExhausted,
87
+ onSuccess: onSuccess as (result: unknown) => void,
88
+ onError: handleError,
89
+ });
100
90
  };
package/src/index.ts CHANGED
@@ -67,18 +67,21 @@ export { enhancePromptWithLanguage, getSupportedLanguages, getLanguageName, Mode
67
67
  export type { ModerationResult, ModerationConfig, SynchronousGenerationInput, SynchronousGenerationConfig } from "./infrastructure/wrappers";
68
68
 
69
69
  export {
70
- useGeneration, usePendingJobs, useBackgroundGeneration, usePhotoGeneration,
71
- useGenerationFlow, useGenerationCallbacksBuilder, useAIFeatureCallbacks,
70
+ useGeneration, usePendingJobs, useBackgroundGeneration,
71
+ useGenerationFlow, useAIFeatureCallbacks,
72
+ useGenerationOrchestrator, useImageGeneration, useVideoGeneration,
73
+ createGenerationError, getAlertMessage, parseError,
72
74
  } from "./presentation/hooks";
73
75
 
74
76
  export type {
75
77
  UseGenerationOptions, UseGenerationReturn, UsePendingJobsOptions, UsePendingJobsReturn,
76
78
  UseBackgroundGenerationOptions, UseBackgroundGenerationReturn, DirectExecutionResult,
77
- UsePhotoGenerationReturn, PhotoGenerationInput, PhotoGenerationResult, PhotoGenerationError,
78
- PhotoGenerationConfig, PhotoGenerationState, PhotoGenerationStatus, UseGenerationFlowOptions,
79
- UseGenerationFlowReturn, CreditType, GenerationExecutionResult, GenerationCallbacksConfig,
80
- GenerationCallbacks, UseGenerationCallbacksBuilderOptions, AIFeatureCallbacksConfig,
81
- AIFeatureCallbacks, AIFeatureGenerationResult,
79
+ UseGenerationFlowOptions, UseGenerationFlowReturn,
80
+ AIFeatureCallbacksConfig, AIFeatureCallbacks, AIFeatureGenerationResult,
81
+ GenerationStrategy, GenerationConfig, GenerationState, OrchestratorStatus,
82
+ GenerationError, GenerationErrorType, AlertMessages, UseGenerationOrchestratorReturn,
83
+ SingleImageInput, DualImageInput, ImageGenerationInput, ImageGenerationConfig,
84
+ DualImageVideoInput, VideoGenerationConfig,
82
85
  } from "./presentation/hooks";
83
86
 
84
87
  export {
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Generation Errors
3
+ * Centralized error handling for generation orchestration
4
+ */
5
+
6
+ import type { GenerationError, GenerationErrorType, AlertMessages } from "./types";
7
+
8
+ export const createGenerationError = (
9
+ type: GenerationErrorType,
10
+ message: string,
11
+ originalError?: Error,
12
+ ): GenerationError => ({
13
+ type,
14
+ message,
15
+ originalError,
16
+ });
17
+
18
+ export const getAlertMessage = (
19
+ error: GenerationError,
20
+ messages: AlertMessages,
21
+ ): string => {
22
+ switch (error.type) {
23
+ case "network":
24
+ return messages.networkError;
25
+ case "credits":
26
+ return messages.creditFailed;
27
+ case "policy":
28
+ return messages.policyViolation;
29
+ case "save":
30
+ return messages.saveFailed;
31
+ default:
32
+ return messages.unknown;
33
+ }
34
+ };
35
+
36
+ export const parseError = (err: unknown): GenerationError => {
37
+ if (isGenerationError(err)) {
38
+ return err;
39
+ }
40
+
41
+ if (err instanceof Error) {
42
+ if (err.name === "ContentPolicyViolationError") {
43
+ return createGenerationError("policy", err.message, err);
44
+ }
45
+ return createGenerationError("unknown", err.message, err);
46
+ }
47
+
48
+ return createGenerationError("unknown", "Generation failed");
49
+ };
50
+
51
+ const isGenerationError = (err: unknown): err is GenerationError => {
52
+ return (
53
+ typeof err === "object" &&
54
+ err !== null &&
55
+ "type" in err &&
56
+ "message" in err
57
+ );
58
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generation Module
3
+ * Feature-agnostic AI generation orchestration
4
+ */
5
+
6
+ // Core orchestrator
7
+ export { useGenerationOrchestrator } from "./orchestrator";
8
+
9
+ // Generic feature hooks
10
+ export { useImageGeneration } from "./useImageGeneration";
11
+ export { useVideoGeneration } from "./useVideoGeneration";
12
+
13
+ // Types
14
+ export type {
15
+ GenerationStrategy,
16
+ GenerationConfig,
17
+ GenerationState,
18
+ OrchestratorStatus,
19
+ GenerationError,
20
+ GenerationErrorType,
21
+ AlertMessages,
22
+ UseGenerationOrchestratorReturn,
23
+ } from "./types";
24
+
25
+ export type {
26
+ SingleImageInput,
27
+ DualImageInput,
28
+ ImageGenerationInput,
29
+ ImageGenerationConfig,
30
+ } from "./useImageGeneration";
31
+
32
+ export type {
33
+ DualImageVideoInput,
34
+ VideoGenerationConfig,
35
+ } from "./useVideoGeneration";
36
+
37
+ // Error utilities
38
+ export {
39
+ createGenerationError,
40
+ getAlertMessage,
41
+ parseError,
42
+ } from "./errors";
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Generation Orchestrator
3
+ * Feature-agnostic hook for AI generation with centralized:
4
+ * - Credit management
5
+ * - Error handling
6
+ * - Alert display
7
+ * - Progress tracking
8
+ */
9
+
10
+ import { useState, useCallback, useRef } from "react";
11
+ import { useOfflineStore, useAlert } from "@umituz/react-native-design-system";
12
+ import { useDeductCredit } from "@umituz/react-native-subscription";
13
+ import { createGenerationError, getAlertMessage, parseError } from "./errors";
14
+ import type {
15
+ GenerationStrategy,
16
+ GenerationConfig,
17
+ GenerationState,
18
+ UseGenerationOrchestratorReturn,
19
+ } from "./types";
20
+
21
+ const INITIAL_STATE = {
22
+ status: "idle" as const,
23
+ isGenerating: false,
24
+ progress: 0,
25
+ result: null,
26
+ error: null,
27
+ };
28
+
29
+ export const useGenerationOrchestrator = <TInput, TResult>(
30
+ strategy: GenerationStrategy<TInput, TResult>,
31
+ config: GenerationConfig,
32
+ ): UseGenerationOrchestratorReturn<TInput, TResult> => {
33
+ const { userId, alertMessages, onCreditsExhausted, onSuccess, onError } =
34
+ config;
35
+
36
+ const [state, setState] = useState<GenerationState<TResult>>(INITIAL_STATE);
37
+ const isGeneratingRef = useRef(false);
38
+ const offlineStore = useOfflineStore();
39
+ const { showError, showSuccess } = useAlert();
40
+ const { checkCredits, deductCredit } = useDeductCredit({
41
+ userId,
42
+ onCreditsExhausted,
43
+ });
44
+
45
+ const generate = useCallback(
46
+ async (input: TInput) => {
47
+ if (isGeneratingRef.current) return;
48
+
49
+ isGeneratingRef.current = true;
50
+ setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
51
+
52
+ try {
53
+ if (!offlineStore.isOnline) {
54
+ throw createGenerationError("network", "No internet connection");
55
+ }
56
+
57
+ const creditCost = strategy.getCreditCost();
58
+ const hasCredits = await checkCredits(creditCost);
59
+ if (!hasCredits) {
60
+ throw createGenerationError("credits", "Insufficient credits");
61
+ }
62
+
63
+ setState((prev) => ({ ...prev, status: "generating", progress: 10 }));
64
+
65
+ const result = await strategy.execute(input, (progress) => {
66
+ setState((prev) => ({ ...prev, progress }));
67
+ });
68
+
69
+ setState((prev) => ({ ...prev, progress: 70 }));
70
+
71
+ if (strategy.save && userId) {
72
+ setState((prev) => ({ ...prev, status: "saving" }));
73
+ try {
74
+ await strategy.save(result, userId);
75
+ } catch (saveErr) {
76
+ throw createGenerationError(
77
+ "save",
78
+ "Failed to save",
79
+ saveErr instanceof Error ? saveErr : undefined,
80
+ );
81
+ }
82
+ }
83
+
84
+ setState((prev) => ({ ...prev, progress: 90 }));
85
+
86
+ await deductCredit(creditCost);
87
+
88
+ setState({
89
+ status: "success",
90
+ isGenerating: false,
91
+ progress: 100,
92
+ result,
93
+ error: null,
94
+ });
95
+
96
+ if (alertMessages.success) {
97
+ void showSuccess("Success", alertMessages.success);
98
+ }
99
+
100
+ onSuccess?.(result);
101
+ } catch (err) {
102
+ const error = parseError(err);
103
+
104
+ setState({
105
+ status: "error",
106
+ isGenerating: false,
107
+ progress: 0,
108
+ result: null,
109
+ error,
110
+ });
111
+
112
+ void showError("Error", getAlertMessage(error, alertMessages));
113
+ onError?.(error);
114
+ } finally {
115
+ isGeneratingRef.current = false;
116
+ }
117
+ },
118
+ [
119
+ strategy,
120
+ userId,
121
+ alertMessages,
122
+ offlineStore.isOnline,
123
+ checkCredits,
124
+ deductCredit,
125
+ showError,
126
+ showSuccess,
127
+ onSuccess,
128
+ onError,
129
+ ],
130
+ );
131
+
132
+ const reset = useCallback(() => {
133
+ setState(INITIAL_STATE);
134
+ isGeneratingRef.current = false;
135
+ }, []);
136
+
137
+ return {
138
+ generate,
139
+ reset,
140
+ status: state.status,
141
+ isGenerating: state.isGenerating,
142
+ progress: state.progress,
143
+ result: state.result,
144
+ error: state.error,
145
+ };
146
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Generation Types
3
+ * Type definitions for feature-agnostic generation orchestration
4
+ */
5
+
6
+ export type OrchestratorStatus =
7
+ | "idle"
8
+ | "checking"
9
+ | "generating"
10
+ | "saving"
11
+ | "success"
12
+ | "error";
13
+
14
+ export interface GenerationStrategy<TInput, TResult> {
15
+ /** Execute the generation */
16
+ execute: (
17
+ input: TInput,
18
+ onProgress?: (progress: number) => void,
19
+ ) => Promise<TResult>;
20
+ /** Credit cost for this generation */
21
+ getCreditCost: () => number;
22
+ /** Optional: Save result to storage */
23
+ save?: (result: TResult, userId: string) => Promise<void>;
24
+ }
25
+
26
+ export interface AlertMessages {
27
+ networkError: string;
28
+ policyViolation: string;
29
+ saveFailed: string;
30
+ creditFailed: string;
31
+ unknown: string;
32
+ success?: string;
33
+ }
34
+
35
+ export interface GenerationConfig {
36
+ userId: string | undefined;
37
+ alertMessages: AlertMessages;
38
+ onCreditsExhausted?: () => void;
39
+ onSuccess?: (result: unknown) => void;
40
+ onError?: (error: GenerationError) => void;
41
+ }
42
+
43
+ export interface GenerationState<TResult> {
44
+ status: OrchestratorStatus;
45
+ isGenerating: boolean;
46
+ progress: number;
47
+ result: TResult | null;
48
+ error: GenerationError | null;
49
+ }
50
+
51
+ export interface GenerationError {
52
+ type: GenerationErrorType;
53
+ message: string;
54
+ originalError?: Error;
55
+ }
56
+
57
+ export type GenerationErrorType =
58
+ | "network"
59
+ | "credits"
60
+ | "policy"
61
+ | "save"
62
+ | "unknown";
63
+
64
+ export interface UseGenerationOrchestratorReturn<TInput, TResult> {
65
+ generate: (input: TInput) => Promise<void>;
66
+ reset: () => void;
67
+ status: OrchestratorStatus;
68
+ isGenerating: boolean;
69
+ progress: number;
70
+ result: TResult | null;
71
+ error: GenerationError | null;
72
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * useImageGeneration Hook
3
+ * Generic image generation hook for ANY image feature
4
+ * Uses centralized orchestrator for credit/error handling
5
+ */
6
+
7
+ import { useMemo, useCallback } from "react";
8
+ import { useGenerationOrchestrator } from "./orchestrator";
9
+ import type { GenerationStrategy, AlertMessages } from "./types";
10
+ import { executeImageFeature } from "../../../infrastructure/services";
11
+ import type { ImageFeatureType } from "../../../domain/interfaces";
12
+ import { createCreationsRepository } from "../../../domains/creations/infrastructure/adapters";
13
+ import type { Creation } from "../../../domains/creations/domain/entities/Creation";
14
+
15
+ /**
16
+ * Generic input for single image features
17
+ */
18
+ export interface SingleImageInput {
19
+ imageBase64: string;
20
+ prompt?: string;
21
+ options?: Record<string, unknown>;
22
+ }
23
+
24
+ /**
25
+ * Generic input for dual image features (face-swap, etc.)
26
+ */
27
+ export interface DualImageInput {
28
+ sourceImageBase64: string;
29
+ targetImageBase64: string;
30
+ options?: Record<string, unknown>;
31
+ }
32
+
33
+ export type ImageGenerationInput = SingleImageInput | DualImageInput;
34
+
35
+ export interface ImageGenerationConfig<TInput extends ImageGenerationInput, TResult> {
36
+ /** Feature type (face-swap, upscale, remove-background, etc.) */
37
+ featureType: ImageFeatureType;
38
+ /** User ID for credit operations */
39
+ userId: string | undefined;
40
+ /** Transform image URL to result type */
41
+ processResult: (imageUrl: string, input: TInput) => TResult;
42
+ /** Build input for executor from generic input */
43
+ buildExecutorInput?: (input: TInput) => {
44
+ imageBase64?: string;
45
+ targetImageBase64?: string;
46
+ prompt?: string;
47
+ options?: Record<string, unknown>;
48
+ };
49
+ /** Optional: Build creation for saving */
50
+ buildCreation?: (result: TResult, input: TInput) => Creation | null;
51
+ /** Credit cost (default: 1) */
52
+ creditCost?: number;
53
+ /** Alert messages for errors */
54
+ alertMessages: AlertMessages;
55
+ /** Callbacks */
56
+ onCreditsExhausted?: () => void;
57
+ onSuccess?: (result: TResult) => void;
58
+ onError?: (error: string) => void;
59
+ }
60
+
61
+ /**
62
+ * Default input builder for single image
63
+ */
64
+ const defaultSingleImageBuilder = (input: SingleImageInput) => ({
65
+ imageBase64: input.imageBase64,
66
+ prompt: input.prompt,
67
+ options: input.options,
68
+ });
69
+
70
+ /**
71
+ * Default input builder for dual image
72
+ */
73
+ const defaultDualImageBuilder = (input: DualImageInput) => ({
74
+ imageBase64: input.sourceImageBase64,
75
+ targetImageBase64: input.targetImageBase64,
76
+ options: input.options,
77
+ });
78
+
79
+ /**
80
+ * Check if input is dual image type
81
+ */
82
+ const isDualImageInput = (input: ImageGenerationInput): input is DualImageInput => {
83
+ return "sourceImageBase64" in input && "targetImageBase64" in input;
84
+ };
85
+
86
+ export const useImageGeneration = <
87
+ TInput extends ImageGenerationInput,
88
+ TResult,
89
+ >(config: ImageGenerationConfig<TInput, TResult>) => {
90
+ const {
91
+ featureType,
92
+ userId,
93
+ processResult,
94
+ buildExecutorInput,
95
+ buildCreation,
96
+ creditCost = 1,
97
+ alertMessages,
98
+ onCreditsExhausted,
99
+ onSuccess,
100
+ onError,
101
+ } = config;
102
+
103
+ const repository = useMemo(
104
+ () => createCreationsRepository("creations"),
105
+ [],
106
+ );
107
+
108
+ const strategy: GenerationStrategy<TInput, TResult> = useMemo(
109
+ () => ({
110
+ execute: async (input, onProgress) => {
111
+ // Build executor input
112
+ const executorInput = buildExecutorInput
113
+ ? buildExecutorInput(input)
114
+ : isDualImageInput(input)
115
+ ? defaultDualImageBuilder(input)
116
+ : defaultSingleImageBuilder(input as SingleImageInput);
117
+
118
+ const result = await executeImageFeature(
119
+ featureType,
120
+ executorInput,
121
+ { onProgress },
122
+ );
123
+
124
+ if (!result.success || !result.imageUrl) {
125
+ throw new Error(result.error || "Image generation failed");
126
+ }
127
+
128
+ return processResult(result.imageUrl, input);
129
+ },
130
+ getCreditCost: () => creditCost,
131
+ save: buildCreation
132
+ ? async (result, uid) => {
133
+ const creation = buildCreation(result, {} as TInput);
134
+ if (creation) {
135
+ await repository.create(uid, creation);
136
+ }
137
+ }
138
+ : undefined,
139
+ }),
140
+ [featureType, processResult, buildExecutorInput, buildCreation, repository, creditCost],
141
+ );
142
+
143
+ const handleError = useCallback(
144
+ (error: { message: string }) => {
145
+ onError?.(error.message);
146
+ },
147
+ [onError],
148
+ );
149
+
150
+ return useGenerationOrchestrator(strategy, {
151
+ userId,
152
+ alertMessages,
153
+ onCreditsExhausted,
154
+ onSuccess: onSuccess as (result: unknown) => void,
155
+ onError: handleError,
156
+ });
157
+ };