@umituz/react-native-ai-generation-content 1.61.32 → 1.61.34

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/domain/entities/error.types.ts +0 -1
  3. package/src/domain/entities/job.types.ts +0 -4
  4. package/src/domain/entities/polling.types.ts +1 -3
  5. package/src/domain/interfaces/app-services.interface.ts +20 -2
  6. package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +2 -1
  7. package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +84 -4
  8. package/src/domains/content-moderation/infrastructure/services/pattern-matcher.service.ts +85 -2
  9. package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +102 -19
  10. package/src/domains/creations/presentation/hooks/useAdvancedFilter.ts +13 -4
  11. package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +10 -9
  12. package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +1 -1
  13. package/src/domains/generation/infrastructure/flow/useFlow.ts +11 -6
  14. package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +2 -3
  15. package/src/exports/infrastructure.ts +24 -1
  16. package/src/exports/presentation.ts +1 -1
  17. package/src/features/image-to-video/presentation/components/index.ts +0 -4
  18. package/src/index.ts +0 -4
  19. package/src/infrastructure/constants/index.ts +14 -2
  20. package/src/infrastructure/constants/polling.constants.ts +34 -0
  21. package/src/infrastructure/constants/storage.constants.ts +25 -0
  22. package/src/infrastructure/constants/validation.constants.ts +40 -0
  23. package/src/infrastructure/logging/logger.ts +185 -0
  24. package/src/infrastructure/services/job-poller.service.ts +13 -28
  25. package/src/infrastructure/utils/status-checker.util.ts +27 -7
  26. package/src/infrastructure/validation/input-validator.ts +406 -0
  27. package/src/presentation/components/AIGenerationForm.tsx +0 -11
  28. package/src/presentation/components/ErrorBoundary.tsx +141 -0
  29. package/src/presentation/components/index.ts +1 -0
  30. package/src/presentation/hooks/use-background-generation.ts +0 -12
  31. package/src/presentation/hooks/useAIFeatureCallbacks.ts +3 -4
  32. package/src/presentation/types/result-config.types.ts +0 -7
@@ -10,8 +10,7 @@ import { useEffect, useRef, useCallback, useState } from "react";
10
10
  import { providerRegistry } from "../../../../../infrastructure/services/provider-registry.service";
11
11
  import { extractResultUrl, type FalResult, type GenerationUrls } from "./generation-result.utils";
12
12
  import { QUEUE_STATUS } from "../../../../../domain/constants/queue-status.constants";
13
-
14
- const POLL_INTERVAL_MS = 3000;
13
+ import { DEFAULT_POLL_INTERVAL_MS } from "../../../../../infrastructure/constants/polling.constants";
15
14
  import type { CreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
16
15
  import type { WizardStrategy } from "../../infrastructure/strategies/wizard-strategy.types";
17
16
  import type { WizardScenarioData } from "./wizard-generation.types";
@@ -179,7 +178,7 @@ export function useVideoQueueGeneration(
179
178
  }
180
179
  }
181
180
 
182
- pollingRef.current = setInterval(() => void pollQueueStatus(), POLL_INTERVAL_MS);
181
+ pollingRef.current = setInterval(() => void pollQueueStatus(), DEFAULT_POLL_INTERVAL_MS);
183
182
  void pollQueueStatus();
184
183
  },
185
184
  [userId, scenario, persistence, strategy, pollQueueStatus, onError],
@@ -28,7 +28,7 @@ export type {
28
28
 
29
29
  // Utils
30
30
  export {
31
- classifyError, isTransientError, isPermanentError, isResultNotReady, calculatePollingInterval,
31
+ classifyError, isPermanentError, isResultNotReady, calculatePollingInterval,
32
32
  checkStatusForErrors, isJobComplete, isJobProcessing, isJobFailed,
33
33
  validateResult, extractOutputUrl, extractOutputUrls, extractVideoUrl, extractThumbnailUrl,
34
34
  extractAudioUrl, extractImageUrls, cleanBase64, addBase64Prefix, preparePhoto, preparePhotos,
@@ -41,6 +41,29 @@ export type {
41
41
  PhotoInput, PreparedImage, ImageSelector, VideoSaver, AlertFunction, FeatureUtilsConfig, VideoAlertFunction,
42
42
  } from "../infrastructure/utils";
43
43
 
44
+ // Validation
45
+ export {
46
+ sanitizeString,
47
+ validateString,
48
+ validateNumber,
49
+ validateURL,
50
+ validateEmail,
51
+ validateBase64,
52
+ validateObject,
53
+ validateArray,
54
+ combineValidationResults,
55
+ sanitizeAndValidate,
56
+ validateAIPrompt,
57
+ validateImageData,
58
+ validateUserId,
59
+ validateCreationId,
60
+ } from "../infrastructure/validation/input-validator";
61
+ export type {
62
+ ValidationResult,
63
+ StringValidationOptions,
64
+ NumericValidationOptions,
65
+ } from "../infrastructure/validation/input-validator";
66
+
44
67
  // Orchestration
45
68
  export type {
46
69
  CreditService, PaywallService, NetworkService, AuthService,
@@ -35,7 +35,7 @@ export {
35
35
  GenerateButton, ResultDisplay, AIGenerationResult, ErrorDisplay, FeatureHeader,
36
36
  AIGenScreenHeader, CreditBadge, PhotoUploadCard, SettingsSheet, StyleSelector,
37
37
  AspectRatioSelector, DurationSelector, GridSelector, StylePresetsGrid, AIGenerationForm,
38
- AIGenerationConfig, ModelSelector,
38
+ AIGenerationConfig, ModelSelector, ErrorBoundary, withErrorBoundary,
39
39
  createAspectRatioOptions, createDurationOptions, createStyleOptions, createStyleOptionsFromConfig,
40
40
  ASPECT_RATIO_IDS, COMMON_DURATIONS,
41
41
  } from "../presentation/components";
@@ -21,10 +21,6 @@ export type {
21
21
  ImageSelectionGridTranslations as ImageToVideoSelectionGridTranslations,
22
22
  } from "./ImageSelectionGrid";
23
23
 
24
- // Hero Section - Using AIGenerationHero from common components
25
- // export { HeroSection as ImageToVideoHeroSection } from "./HeroSection";
26
- // export type { HeroSectionProps as ImageToVideoHeroSectionProps } from "./HeroSection";
27
-
28
24
  // Action Components
29
25
  export { GenerateButton as ImageToVideoGenerateButton } from "../../../../presentation/components/buttons";
30
26
  export type { GenerateButtonProps as ImageToVideoGenerateButtonProps } from "../../../../presentation/components/buttons";
package/src/index.ts CHANGED
@@ -3,10 +3,6 @@
3
3
  * Provider-agnostic AI generation orchestration
4
4
  */
5
5
 
6
- if (typeof __DEV__ !== "undefined" && __DEV__) {
7
- console.log("📍 [LIFECYCLE] @umituz/react-native-ai-generation-content/index.ts - Module loading");
8
- }
9
-
10
6
  // Domain Layer
11
7
  export * from "./exports/domain";
12
8
 
@@ -2,8 +2,20 @@
2
2
  * Infrastructure Constants
3
3
  */
4
4
 
5
- /** Video generation timeout in milliseconds (5 minutes) */
5
+ // Polling Constants
6
+ export * from "./polling.constants";
7
+
8
+ // Validation Constants
9
+ export * from "./validation.constants";
10
+
11
+ // Content Moderation Constants
12
+ export * from "./content.constants";
13
+
14
+ // Storage Constants
15
+ export * from "./storage.constants";
16
+
17
+ /** Video generation timeout in milliseconds (5 minutes) - @deprecated Use DEFAULT_MAX_POLL_TIME_MS instead */
6
18
  export const VIDEO_TIMEOUT_MS = 300000;
7
19
 
8
- /** Maximum consecutive transient errors before failing */
20
+ /** Maximum consecutive transient errors before failing - @deprecated Use DEFAULT_MAX_CONSECUTIVE_ERRORS instead */
9
21
  export const MAX_TRANSIENT_ERRORS = 5;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Polling Constants
3
+ * All timeout, interval, and retry related constants
4
+ */
5
+
6
+ /** Default polling interval for job status checks (milliseconds) */
7
+ export const DEFAULT_POLL_INTERVAL_MS = 3000;
8
+
9
+ /** Gallery polling interval (slower than wizard) */
10
+ export const GALLERY_POLL_INTERVAL_MS = 5000;
11
+
12
+ /** Maximum number of polling attempts before timeout */
13
+ export const DEFAULT_MAX_POLL_ATTEMPTS = 100;
14
+
15
+ /** Maximum consecutive transient errors before aborting */
16
+ export const DEFAULT_MAX_CONSECUTIVE_ERRORS = 5;
17
+
18
+ /** Maximum total time for polling (milliseconds) - ~5 minutes */
19
+ export const DEFAULT_MAX_POLL_TIME_MS = 5 * 60 * 1000;
20
+
21
+ /** Minimum backoff delay (milliseconds) */
22
+ export const MIN_BACKOFF_DELAY_MS = 1000;
23
+
24
+ /** Maximum backoff delay (milliseconds) */
25
+ export const MAX_BACKOFF_DELAY_MS = 30000;
26
+
27
+ /** Exponential backoff base multiplier */
28
+ export const BACKOFF_MULTIPLIER = 1.5;
29
+
30
+ /** Progress percentage to report when job completes */
31
+ export const COMPLETION_PROGRESS = 100;
32
+
33
+ /** Initial progress percentage */
34
+ export const INITIAL_PROGRESS = 0;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Storage Constants
3
+ * Database and storage related constants
4
+ */
5
+
6
+ /** Maximum number of creations to fetch in one query */
7
+ export const MAX_CREATIONS_FETCH_LIMIT = 100;
8
+
9
+ /** Default number of creations per page */
10
+ export const DEFAULT_CREATIONS_PAGE_SIZE = 20;
11
+
12
+ /** Maximum number of logs to keep in memory */
13
+ export const MAX_LOG_ENTRIES = 1000;
14
+
15
+ /** Timeout for database operations (milliseconds) */
16
+ export const DB_OPERATION_TIMEOUT_MS = 10000;
17
+
18
+ /** Maximum cache size for frequently accessed data */
19
+ export const MAX_CACHE_SIZE = 100;
20
+
21
+ /** Cache TTL for creations (milliseconds) - 5 minutes */
22
+ export const CREATIONS_CACHE_TTL_MS = 5 * 60 * 1000;
23
+
24
+ /** Cache TTL for user data (milliseconds) - 10 minutes */
25
+ export const USER_DATA_CACHE_TTL_MS = 10 * 60 * 1000;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Validation Constants
3
+ * All validation limits and constraints
4
+ */
5
+
6
+ /** Maximum length for AI prompt text */
7
+ export const MAX_PROMPT_LENGTH = 10000;
8
+
9
+ /** Minimum length for AI prompt text */
10
+ export const MIN_PROMPT_LENGTH = 1;
11
+
12
+ /** Maximum length for user ID */
13
+ export const MAX_USER_ID_LENGTH = 128;
14
+
15
+ /** Maximum length for creation ID */
16
+ export const MAX_CREATION_ID_LENGTH = 128;
17
+
18
+ /** Maximum base64 string length (before encoding) */
19
+ export const MAX_BASE64_SIZE_MB = 20;
20
+
21
+ /** Maximum regex pattern length for validation */
22
+ export const MAX_PATTERN_LENGTH = 1000;
23
+
24
+ /** Maximum duration for video generation (seconds) */
25
+ export const MAX_VIDEO_DURATION_SECONDS = 60;
26
+
27
+ /** Minimum duration for video generation (seconds) */
28
+ export const MIN_VIDEO_DURATION_SECONDS = 3;
29
+
30
+ /** Maximum image file size (MB) */
31
+ export const MAX_IMAGE_SIZE_MB = 10;
32
+
33
+ /** Maximum URL length */
34
+ export const MAX_URL_LENGTH = 2048;
35
+
36
+ /** Maximum number of images in multi-image generation */
37
+ export const MAX_MULTI_IMAGE_COUNT = 10;
38
+
39
+ /** Minimum number of images for multi-image generation */
40
+ export const MIN_MULTI_IMAGE_COUNT = 2;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Production-Ready Logging Utility
3
+ * Provides structured logging with levels and context
4
+ */
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ export enum LogLevel {
9
+ DEBUG = 0,
10
+ INFO = 1,
11
+ WARN = 2,
12
+ ERROR = 3,
13
+ NONE = 4,
14
+ }
15
+
16
+ export interface LogContext {
17
+ readonly [key: string]: unknown;
18
+ }
19
+
20
+ export interface LogEntry {
21
+ readonly level: LogLevel;
22
+ readonly timestamp: number;
23
+ readonly message: string;
24
+ readonly context?: LogContext;
25
+ readonly error?: Error;
26
+ }
27
+
28
+ class Logger {
29
+ private minLevel: LogLevel;
30
+ private logs: LogEntry[] = [];
31
+ private maxLogs = 1000;
32
+
33
+ constructor() {
34
+ // In production, only log WARN and ERROR
35
+ this.minLevel =
36
+ typeof __DEV__ !== "undefined" && __DEV__ ? LogLevel.DEBUG : LogLevel.WARN;
37
+ }
38
+
39
+ setMinLevel(level: LogLevel): void {
40
+ this.minLevel = level;
41
+ }
42
+
43
+ private shouldLog(level: LogLevel): boolean {
44
+ return level >= this.minLevel;
45
+ }
46
+
47
+ private addLog(entry: LogEntry): void {
48
+ this.logs.push(entry);
49
+ // Keep only last maxLogs entries
50
+ if (this.logs.length > this.maxLogs) {
51
+ this.logs.shift();
52
+ }
53
+ }
54
+
55
+ private formatMessage(entry: LogEntry): string {
56
+ const levelName = LogLevel[entry.level];
57
+ const timestamp = new Date(entry.timestamp).toISOString();
58
+ const contextStr = entry.context
59
+ ? ` ${JSON.stringify(entry.context)}`
60
+ : "";
61
+ const errorStr = entry.error ? ` ${entry.error.message}` : "";
62
+ return `[${timestamp}] [${levelName}] ${entry.message}${contextStr}${errorStr}`;
63
+ }
64
+
65
+ debug(message: string, context?: LogContext): void {
66
+ if (!this.shouldLog(LogLevel.DEBUG)) return;
67
+
68
+ const entry: LogEntry = {
69
+ level: LogLevel.DEBUG,
70
+ timestamp: Date.now(),
71
+ message,
72
+ context,
73
+ };
74
+
75
+ this.addLog(entry);
76
+
77
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
78
+ console.log(`[DEBUG] ${message}`, context || "");
79
+ }
80
+ }
81
+
82
+ info(message: string, context?: LogContext): void {
83
+ if (!this.shouldLog(LogLevel.INFO)) return;
84
+
85
+ const entry: LogEntry = {
86
+ level: LogLevel.INFO,
87
+ timestamp: Date.now(),
88
+ message,
89
+ context,
90
+ };
91
+
92
+ this.addLog(entry);
93
+
94
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
95
+ console.info(`[INFO] ${message}`, context || "");
96
+ }
97
+ }
98
+
99
+ warn(message: string, context?: LogContext): void {
100
+ if (!this.shouldLog(LogLevel.WARN)) return;
101
+
102
+ const entry: LogEntry = {
103
+ level: LogLevel.WARN,
104
+ timestamp: Date.now(),
105
+ message,
106
+ context,
107
+ };
108
+
109
+ this.addLog(entry);
110
+ console.warn(`[WARN] ${message}`, context || "");
111
+ }
112
+
113
+ error(message: string, error?: Error | unknown, context?: LogContext): void {
114
+ if (!this.shouldLog(LogLevel.ERROR)) return;
115
+
116
+ const errorObj =
117
+ error instanceof Error ? error : error ? new Error(String(error)) : undefined;
118
+
119
+ const entry: LogEntry = {
120
+ level: LogLevel.ERROR,
121
+ timestamp: Date.now(),
122
+ message,
123
+ context,
124
+ error: errorObj,
125
+ };
126
+
127
+ this.addLog(entry);
128
+ console.error(`[ERROR] ${message}`, errorObj || "", context || "");
129
+ }
130
+
131
+ getLogs(level?: LogLevel): readonly LogEntry[] {
132
+ if (level !== undefined) {
133
+ return this.logs.filter((log) => log.level >= level);
134
+ }
135
+ return this.logs;
136
+ }
137
+
138
+ clearLogs(): void {
139
+ this.logs = [];
140
+ }
141
+
142
+ // Convenience methods for specific domains
143
+ processing(message: string, context?: LogContext): void {
144
+ this.debug(`[Processing] ${message}`, context);
145
+ }
146
+
147
+ generation(message: string, context?: LogContext): void {
148
+ this.debug(`[Generation] ${message}`, context);
149
+ }
150
+
151
+ polling(message: string, context?: LogContext): void {
152
+ this.debug(`[Polling] ${message}`, context);
153
+ }
154
+
155
+ moderation(message: string, context?: LogContext): void {
156
+ this.info(`[Moderation] ${message}`, context);
157
+ }
158
+
159
+ network(message: string, context?: LogContext): void {
160
+ this.info(`[Network] ${message}`, context);
161
+ }
162
+
163
+ storage(message: string, context?: LogContext): void {
164
+ this.debug(`[Storage] ${message}`, context);
165
+ }
166
+
167
+ validation(message: string, context?: LogContext): void {
168
+ this.warn(`[Validation] ${message}`, context);
169
+ }
170
+ }
171
+
172
+ // Singleton instance
173
+ export const logger = new Logger();
174
+
175
+ // Development helper
176
+ export function setDevelopmentMode(): void {
177
+ logger.setMinLevel(LogLevel.DEBUG);
178
+ }
179
+
180
+ // Production helper
181
+ export function setProductionMode(): void {
182
+ logger.setMinLevel(LogLevel.WARN);
183
+ }
184
+
185
+ export type { LogEntry, LogContext };
@@ -8,11 +8,8 @@ import { DEFAULT_POLLING_CONFIG } from "../../domain/entities";
8
8
  import { calculatePollingInterval } from "../utils/polling-interval.util";
9
9
  import { checkStatusForErrors, isJobComplete } from "../utils/status-checker.util";
10
10
  import { validateResult } from "../utils/result-validator.util";
11
- import { isTransientError } from "../utils/error-classifier.util";
12
11
  import type { PollJobOptions, PollJobResult } from "./job-poller.types";
13
12
 
14
- declare const __DEV__: boolean;
15
-
16
13
  /**
17
14
  * Poll job until completion with exponential backoff
18
15
  * Only reports 100% on actual completion
@@ -31,18 +28,28 @@ export async function pollJob<T = unknown>(
31
28
  } = options;
32
29
 
33
30
  const pollingConfig = { ...DEFAULT_POLLING_CONFIG, ...config };
34
- const { maxAttempts, maxConsecutiveErrors } = pollingConfig;
31
+ const { maxAttempts, maxTotalTimeMs } = pollingConfig;
35
32
 
36
33
  const startTime = Date.now();
37
- let consecutiveTransientErrors = 0;
38
34
 
39
35
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
36
+ // Check total time limit
37
+ const elapsedMs = Date.now() - startTime;
38
+ if (maxTotalTimeMs && elapsedMs >= maxTotalTimeMs) {
39
+ return {
40
+ success: false,
41
+ error: new Error(`Polling timeout after ${elapsedMs}ms`),
42
+ attempts: attempt + 1,
43
+ elapsedMs,
44
+ };
45
+ }
46
+
40
47
  if (signal?.aborted) {
41
48
  return {
42
49
  success: false,
43
50
  error: new Error("Polling aborted"),
44
51
  attempts: attempt + 1,
45
- elapsedMs: Date.now() - startTime,
52
+ elapsedMs,
46
53
  };
47
54
  }
48
55
 
@@ -91,28 +98,6 @@ export async function pollJob<T = unknown>(
91
98
  };
92
99
  }
93
100
  } catch (error) {
94
- if (isTransientError(error)) {
95
- consecutiveTransientErrors++;
96
-
97
- if (consecutiveTransientErrors >= maxConsecutiveErrors) {
98
- return {
99
- success: false,
100
- error: error instanceof Error ? error : new Error(String(error)),
101
- attempts: attempt + 1,
102
- elapsedMs: Date.now() - startTime,
103
- };
104
- }
105
-
106
- if (attempt < maxAttempts - 1) {
107
- if (typeof __DEV__ !== "undefined" && __DEV__) {
108
- console.log(
109
- `[JobPoller] Transient error, retrying (${attempt + 1}/${maxAttempts})`,
110
- );
111
- }
112
- continue;
113
- }
114
- }
115
-
116
101
  return {
117
102
  success: false,
118
103
  error: error instanceof Error ? error : new Error(String(error)),
@@ -12,6 +12,27 @@ export interface StatusCheckResult {
12
12
  shouldStop: boolean;
13
13
  }
14
14
 
15
+ /**
16
+ * Type guard to check if object has a property
17
+ */
18
+ function hasProperty<K extends PropertyKey>(
19
+ obj: unknown,
20
+ prop: K
21
+ ): obj is Record<K, unknown> {
22
+ return typeof obj === "object" && obj !== null && prop in obj;
23
+ }
24
+
25
+ /**
26
+ * Safely get a string property from an object
27
+ */
28
+ function safeString(obj: unknown, key: PropertyKey): string {
29
+ if (hasProperty(obj, key)) {
30
+ const value = obj[key];
31
+ return typeof value === "string" ? value : String(value ?? "");
32
+ }
33
+ return "";
34
+ }
35
+
15
36
  /**
16
37
  * Check job status response for errors
17
38
  * Detects errors even if status is not explicitly FAILED
@@ -19,16 +40,15 @@ export interface StatusCheckResult {
19
40
  export function checkStatusForErrors(
20
41
  status: JobStatus | Record<string, unknown>,
21
42
  ): StatusCheckResult {
22
- const statusString = String(
23
- (status as Record<string, unknown>)?.status || "",
24
- ).toUpperCase();
43
+ // Safely extract status string
44
+ const statusString = safeString(status, "status").toUpperCase();
25
45
 
26
46
  // Check for error in status response fields
27
47
  const statusError =
28
- (status as Record<string, unknown>)?.error ||
29
- (status as Record<string, unknown>)?.detail ||
30
- (status as Record<string, unknown>)?.message;
31
- const hasStatusError = !!statusError;
48
+ safeString(status, "error") ||
49
+ safeString(status, "detail") ||
50
+ safeString(status, "message");
51
+ const hasStatusError = statusError.length > 0;
32
52
 
33
53
  // Check logs array for ERROR/FATAL level logs
34
54
  const rawLogs = (status as JobStatus)?.logs;