@umituz/react-native-ai-generation-content 1.17.67 → 1.17.69
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
|
@@ -20,7 +20,7 @@ export interface ProjectData {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface TextToVideoCallbacks {
|
|
23
|
-
onCreditCheck?: (cost: number) =>
|
|
23
|
+
onCreditCheck?: (cost: number) => Promise<boolean>;
|
|
24
24
|
onAuthCheck?: () => boolean;
|
|
25
25
|
onModeration?: (prompt: string) => Promise<VideoModerationResult>;
|
|
26
26
|
onProjectCreate?: (data: ProjectData) => Promise<void>;
|
package/src/index.ts
CHANGED
|
@@ -516,3 +516,8 @@ export * from "./features/text-to-voice";
|
|
|
516
516
|
|
|
517
517
|
export * from "./features/hd-touch-up";
|
|
518
518
|
|
|
519
|
+
// =============================================================================
|
|
520
|
+
// INFRASTRUCTURE - Orchestration
|
|
521
|
+
// =============================================================================
|
|
522
|
+
|
|
523
|
+
export * from "./infrastructure/orchestration";
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Generation Orchestrator
|
|
3
|
+
* Single Responsibility: Orchestrate common AI generation business operations
|
|
4
|
+
*
|
|
5
|
+
* Generic orchestrator for 100+ apps with dependency injection
|
|
6
|
+
* Handles: Network check, content moderation, credit processing, refunds
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { contentModerationService, ContentPolicyViolationError } from '../../domains/content-moderation';
|
|
10
|
+
|
|
11
|
+
declare const __DEV__: boolean;
|
|
12
|
+
|
|
13
|
+
export interface CreditService {
|
|
14
|
+
getBalance(userId: string): Promise<number>;
|
|
15
|
+
deduct(userId: string, amount: number): Promise<boolean>;
|
|
16
|
+
add(userId: string, amount: number): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PaywallService {
|
|
20
|
+
show(requiredCredits: number): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface NetworkService {
|
|
24
|
+
isOnline(): boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AuthService {
|
|
28
|
+
getUserId(): string | null;
|
|
29
|
+
isAuthenticated(): boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface GenerationMetadata {
|
|
33
|
+
creditCost?: number;
|
|
34
|
+
numImages?: number;
|
|
35
|
+
duration?: number;
|
|
36
|
+
isPremium?: boolean;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type GenerationCapability =
|
|
41
|
+
| 'text-to-image'
|
|
42
|
+
| 'text-to-video'
|
|
43
|
+
| 'image-to-video'
|
|
44
|
+
| 'text-to-voice';
|
|
45
|
+
|
|
46
|
+
export class NetworkUnavailableError extends Error {
|
|
47
|
+
constructor() {
|
|
48
|
+
super('No internet connection. Please check your network and try again.');
|
|
49
|
+
this.name = 'NetworkUnavailableError';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class InsufficientCreditsError extends Error {
|
|
54
|
+
constructor(
|
|
55
|
+
public required: number,
|
|
56
|
+
public current: number,
|
|
57
|
+
public capability: string,
|
|
58
|
+
) {
|
|
59
|
+
super(
|
|
60
|
+
`Insufficient credits. Required: ${required}, Current: ${current}`,
|
|
61
|
+
);
|
|
62
|
+
this.name = 'InsufficientCreditsError';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class AuthenticationRequiredError extends Error {
|
|
67
|
+
constructor() {
|
|
68
|
+
super('User not authenticated. This operation requires authentication.');
|
|
69
|
+
this.name = 'AuthenticationRequiredError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface OrchestratorConfig {
|
|
74
|
+
creditService: CreditService;
|
|
75
|
+
paywallService: PaywallService;
|
|
76
|
+
networkService: NetworkService;
|
|
77
|
+
authService: AuthService;
|
|
78
|
+
calculateCreditCost: (
|
|
79
|
+
capability: GenerationCapability,
|
|
80
|
+
metadata?: GenerationMetadata,
|
|
81
|
+
) => number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class GenerationOrchestrator {
|
|
85
|
+
constructor(private config: OrchestratorConfig) {}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Require authenticated user - throws if not authenticated
|
|
89
|
+
*/
|
|
90
|
+
private requireAuthenticatedUser(): string {
|
|
91
|
+
const { authService } = this.config;
|
|
92
|
+
const isAuthenticated = authService.isAuthenticated();
|
|
93
|
+
const userId = authService.getUserId();
|
|
94
|
+
|
|
95
|
+
if (!isAuthenticated || !userId) {
|
|
96
|
+
throw new AuthenticationRequiredError();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return userId;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check network availability - throws if offline
|
|
104
|
+
*/
|
|
105
|
+
requireNetwork(): void {
|
|
106
|
+
if (!this.config.networkService.isOnline()) {
|
|
107
|
+
throw new NetworkUnavailableError();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Moderate content before generation
|
|
113
|
+
*/
|
|
114
|
+
async moderateContent(
|
|
115
|
+
_userId: string,
|
|
116
|
+
contentType: 'text' | 'image',
|
|
117
|
+
content: string,
|
|
118
|
+
metadata?: GenerationMetadata,
|
|
119
|
+
): Promise<void> {
|
|
120
|
+
const actualUserId = this.requireAuthenticatedUser();
|
|
121
|
+
|
|
122
|
+
const moderationResult = await contentModerationService.moderate({
|
|
123
|
+
userId: actualUserId,
|
|
124
|
+
contentType,
|
|
125
|
+
content,
|
|
126
|
+
metadata,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (!moderationResult.isAllowed) {
|
|
130
|
+
throw new ContentPolicyViolationError(
|
|
131
|
+
moderationResult.violations,
|
|
132
|
+
moderationResult.violations[0]?.suggestion,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Calculate credit cost for generation
|
|
139
|
+
*/
|
|
140
|
+
calculateCreditCost(
|
|
141
|
+
capability: GenerationCapability,
|
|
142
|
+
metadata?: GenerationMetadata,
|
|
143
|
+
): number {
|
|
144
|
+
if (metadata?.creditCost && typeof metadata.creditCost === 'number') {
|
|
145
|
+
return metadata.creditCost;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return this.config.calculateCreditCost(capability, metadata);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check credits BEFORE generation starts
|
|
153
|
+
* Shows paywall if insufficient
|
|
154
|
+
* @returns true if can proceed, false if paywall was shown
|
|
155
|
+
*/
|
|
156
|
+
async checkCreditsAndShowPaywall(
|
|
157
|
+
capability: GenerationCapability,
|
|
158
|
+
metadata?: GenerationMetadata,
|
|
159
|
+
): Promise<boolean> {
|
|
160
|
+
const creditCost = this.calculateCreditCost(capability, metadata);
|
|
161
|
+
const actualUserId = this.requireAuthenticatedUser();
|
|
162
|
+
|
|
163
|
+
const currentCredits =
|
|
164
|
+
await this.config.creditService.getBalance(actualUserId);
|
|
165
|
+
|
|
166
|
+
if (currentCredits < creditCost) {
|
|
167
|
+
this.config.paywallService.show(creditCost);
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Process credits (deduct) for generation
|
|
176
|
+
* @returns deducted amount
|
|
177
|
+
*/
|
|
178
|
+
async processCredits(
|
|
179
|
+
_userId: string,
|
|
180
|
+
capability: GenerationCapability,
|
|
181
|
+
metadata?: GenerationMetadata,
|
|
182
|
+
): Promise<number> {
|
|
183
|
+
const actualUserId = this.requireAuthenticatedUser();
|
|
184
|
+
const creditCost = this.calculateCreditCost(capability, metadata);
|
|
185
|
+
|
|
186
|
+
const currentCredits =
|
|
187
|
+
await this.config.creditService.getBalance(actualUserId);
|
|
188
|
+
|
|
189
|
+
const success = await this.config.creditService.deduct(
|
|
190
|
+
actualUserId,
|
|
191
|
+
creditCost,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
if (!success) {
|
|
195
|
+
this.config.paywallService.show(creditCost);
|
|
196
|
+
throw new InsufficientCreditsError(
|
|
197
|
+
creditCost,
|
|
198
|
+
currentCredits,
|
|
199
|
+
capability,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return creditCost;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Refund credits after generation failure
|
|
208
|
+
* Default behavior: REFUND all errors except user-caused ones
|
|
209
|
+
*/
|
|
210
|
+
async refundCreditsIfApplicable(
|
|
211
|
+
_userId: string,
|
|
212
|
+
amount: number,
|
|
213
|
+
error: unknown,
|
|
214
|
+
): Promise<void> {
|
|
215
|
+
if (amount <= 0) return;
|
|
216
|
+
|
|
217
|
+
// Non-refundable errors: User's fault or policy violations
|
|
218
|
+
const isNonRefundable =
|
|
219
|
+
error instanceof ContentPolicyViolationError ||
|
|
220
|
+
error instanceof InsufficientCreditsError ||
|
|
221
|
+
(error instanceof Error &&
|
|
222
|
+
(error.message.toLowerCase().includes('invalid input') ||
|
|
223
|
+
error.message.toLowerCase().includes('cancelled') ||
|
|
224
|
+
error.message.toLowerCase().includes('user cancel')));
|
|
225
|
+
|
|
226
|
+
if (__DEV__) {
|
|
227
|
+
// eslint-disable-next-line no-console
|
|
228
|
+
console.log('[GenerationOrchestrator] 🔄 Refund check:', {
|
|
229
|
+
amount,
|
|
230
|
+
isNonRefundable,
|
|
231
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
232
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Default: REFUND all errors except user-caused ones
|
|
237
|
+
if (!isNonRefundable) {
|
|
238
|
+
const actualUserId = this.requireAuthenticatedUser();
|
|
239
|
+
|
|
240
|
+
if (__DEV__) {
|
|
241
|
+
// eslint-disable-next-line no-console
|
|
242
|
+
console.log('[GenerationOrchestrator] ✅ Refunding credits:', {
|
|
243
|
+
userId: actualUserId,
|
|
244
|
+
amount,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
await this.config.creditService.add(actualUserId, amount);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|