@umituz/react-native-ai-generation-content 1.86.0 → 1.87.1

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.86.0",
3
+ "version": "1.87.1",
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",
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Generation Execution Utilities
3
+ * Shared utilities for image and audio generation executors to eliminate code duplication
4
+ */
5
+
6
+ import { generateUUID } from "@umituz/react-native-design-system/uuid";
7
+
8
+ /**
9
+ * Resolve type from config - handles both static string and function types
10
+ */
11
+ export function resolveType<P>(
12
+ typeConfig: string | ((params: P) => string),
13
+ params: P
14
+ ): string {
15
+ return typeof typeConfig === "function" ? typeConfig(params) : typeConfig;
16
+ }
17
+
18
+ /**
19
+ * Handle credit refund with error logging
20
+ */
21
+ export async function handleCreditRefund(
22
+ refundCredits: (amount: number) => Promise<void> | Promise<boolean>,
23
+ cost: number,
24
+ typeLabel: string
25
+ ): Promise<void> {
26
+ try {
27
+ await refundCredits(cost);
28
+ } catch {
29
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
30
+ console.error(`[${typeLabel}] Refund failed`);
31
+ }
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Log generation error in development
37
+ */
38
+ export function logGenerationError(
39
+ typeLabel: string,
40
+ error: unknown
41
+ ): void {
42
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
43
+ console.error(`[${typeLabel}]`, error);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Generate unique creation ID using Design System UUID
49
+ */
50
+ export function generateCreationId(): string {
51
+ return generateUUID();
52
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Repository Singleton
3
+ * Prevents multiple repository instances across hooks
4
+ */
5
+
6
+ import { createCreationsRepository } from "../../../domains/creations/infrastructure/adapters";
7
+
8
+ const REPO_COLLECTION = "creations";
9
+
10
+ let repositoryInstance: ReturnType<typeof createCreationsRepository> | null = null;
11
+
12
+ export function getCreationsRepository() {
13
+ if (!repositoryInstance) {
14
+ repositoryInstance = createCreationsRepository(REPO_COLLECTION);
15
+ }
16
+ return repositoryInstance;
17
+ }
@@ -9,11 +9,12 @@
9
9
  * - buildMetadata: creation metadata
10
10
  */
11
11
 
12
- import { useState, useCallback, useMemo } from "react";
12
+ import { useState, useCallback } from "react";
13
13
  import { useGenerationServices } from "../../../infrastructure/providers/generation-services.provider";
14
14
  import { resolveProvider } from "../../../infrastructure/services/provider-resolver";
15
- import { createCreationsRepository } from "../../../domains/creations/infrastructure/adapters";
15
+ import { getCreationsRepository } from "./repositorySingleton";
16
16
  import type { GenerationTarget } from "./useImageGenerationExecutor";
17
+ import { handleCreditRefund, logGenerationError, generateCreationId } from "./generation-execution.utils";
17
18
 
18
19
  /** Domain-specific audio generation input returned by buildInput */
19
20
  export interface AudioGenerationInput {
@@ -59,10 +60,7 @@ export function useAudioGenerationExecutor<P>(
59
60
  ): AudioGenerationExecutorReturn<P> {
60
61
  const { userId, deductCredits, refundCredits, onGenerationSuccess } =
61
62
  useGenerationServices();
62
- const repository = useMemo(
63
- () => createCreationsRepository("creations"),
64
- [],
65
- );
63
+ const repository = getCreationsRepository();
66
64
  const [error, setError] = useState<string | null>(null);
67
65
  const [isLoading, setIsLoading] = useState(false);
68
66
 
@@ -95,7 +93,7 @@ export function useAudioGenerationExecutor<P>(
95
93
  if (!audioUrl) throw new Error("No audio returned");
96
94
 
97
95
  await repository.create(userId, {
98
- id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
96
+ id: generateCreationId(),
99
97
  type: config.type,
100
98
  uri: audioUrl,
101
99
  createdAt: new Date(),
@@ -117,18 +115,12 @@ export function useAudioGenerationExecutor<P>(
117
115
  const message =
118
116
  err instanceof Error ? err.message : "Generation failed";
119
117
  setError(message);
118
+
120
119
  if (deducted) {
121
- try {
122
- await refundCredits(cost);
123
- } catch {
124
- if (typeof __DEV__ !== "undefined" && __DEV__) {
125
- console.error(`[${config.type}] Refund failed`);
126
- }
127
- }
128
- }
129
- if (typeof __DEV__ !== "undefined" && __DEV__) {
130
- console.error(`[${config.type}]`, err);
120
+ await handleCreditRefund(refundCredits, cost, config.type);
131
121
  }
122
+
123
+ logGenerationError(config.type, err);
132
124
  return null;
133
125
  } finally {
134
126
  setIsLoading(false);
@@ -11,11 +11,12 @@
11
11
  * Auth and credit services come from GenerationServicesProvider context.
12
12
  */
13
13
 
14
- import { useState, useCallback, useMemo } from "react";
14
+ import { useState, useCallback } from "react";
15
15
  import { useGenerationServices } from "../../../infrastructure/providers/generation-services.provider";
16
16
  import { resolveProvider } from "../../../infrastructure/services/provider-resolver";
17
- import { createCreationsRepository } from "../../../domains/creations/infrastructure/adapters";
17
+ import { getCreationsRepository } from "./repositorySingleton";
18
18
  import { preprocessImageInputs } from "../../../infrastructure/utils/image-input-preprocessor.util";
19
+ import { resolveType, handleCreditRefund, logGenerationError, generateCreationId } from "./generation-execution.utils";
19
20
 
20
21
  /** Target for generation: which model on which provider */
21
22
  export interface GenerationTarget {
@@ -65,10 +66,7 @@ export function useImageGenerationExecutor<P>(
65
66
  ): ImageGenerationExecutorReturn<P> {
66
67
  const { userId, deductCredits, refundCredits, onGenerationSuccess } =
67
68
  useGenerationServices();
68
- const repository = useMemo(
69
- () => createCreationsRepository("creations"),
70
- [],
71
- );
69
+ const repository = getCreationsRepository();
72
70
  const [error, setError] = useState<string | null>(null);
73
71
  const [isLoading, setIsLoading] = useState(false);
74
72
 
@@ -101,8 +99,8 @@ export function useImageGenerationExecutor<P>(
101
99
  if (!imageUrl) throw new Error("No image returned");
102
100
 
103
101
  await repository.create(userId, {
104
- id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
105
- type: typeof config.type === "function" ? config.type(params) : config.type,
102
+ id: generateCreationId(),
103
+ type: resolveType(config.type, params),
106
104
  uri: imageUrl,
107
105
  createdAt: new Date(),
108
106
  isShared: false,
@@ -123,19 +121,13 @@ export function useImageGenerationExecutor<P>(
123
121
  const message =
124
122
  err instanceof Error ? err.message : "Generation failed";
125
123
  setError(message);
126
- const typeLabel = typeof config.type === "function" ? config.type(params) : config.type;
124
+ const typeLabel = resolveType(config.type, params);
125
+
127
126
  if (deducted) {
128
- try {
129
- await refundCredits(cost);
130
- } catch {
131
- if (typeof __DEV__ !== "undefined" && __DEV__) {
132
- console.error(`[${typeLabel}] Refund failed`);
133
- }
134
- }
135
- }
136
- if (typeof __DEV__ !== "undefined" && __DEV__) {
137
- console.error(`[${typeLabel}]`, err);
127
+ await handleCreditRefund(refundCredits, cost, typeLabel);
138
128
  }
129
+
130
+ logGenerationError(typeLabel, err);
139
131
  return null;
140
132
  } finally {
141
133
  setIsLoading(false);