@umituz/react-native-ai-generation-content 1.90.2 → 1.90.4

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 (171) hide show
  1. package/package.json +3 -3
  2. package/src/domain/interfaces/app-services-auth.interface.ts +27 -0
  3. package/src/domain/interfaces/app-services-composite.interface.ts +29 -0
  4. package/src/domain/interfaces/app-services-optional.interface.ts +42 -0
  5. package/src/domain/interfaces/app-services.interface.ts +0 -79
  6. package/src/domain/interfaces/index.ts +3 -0
  7. package/src/domains/background/infrastructure/services/job-poller-index.ts +7 -0
  8. package/src/domains/background/infrastructure/services/job-poller-utils.ts +127 -0
  9. package/src/domains/background/infrastructure/services/job-poller.service.ts +85 -140
  10. package/src/domains/background/infrastructure/utils/polling-interval.util.ts +1 -1
  11. package/src/domains/background/presentation/hooks/use-background-generation.ts +1 -1
  12. package/src/domains/content-moderation/index.ts +7 -13
  13. package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +1 -1
  14. package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +34 -8
  15. package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +15 -4
  16. package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +34 -8
  17. package/src/domains/content-moderation/infrastructure/services/moderators/voice.moderator.ts +19 -8
  18. package/src/domains/content-moderation/infrastructure/services/pattern-matcher.service.ts +1 -2
  19. package/src/domains/creations/domain/types/creation-categories.constants.ts +57 -0
  20. package/src/domains/creations/domain/types/creation-categories.helpers.ts +67 -0
  21. package/src/domains/creations/domain/types/creation-categories.ts +7 -114
  22. package/src/domains/creations/domain/utils/creation-display.util.ts +1 -1
  23. package/src/domains/creations/domain/utils/status-helpers.ts +1 -1
  24. package/src/domains/creations/presentation/hooks/creation-validators.ts +31 -29
  25. package/src/domains/creations/presentation/hooks/job-poller-index.ts +10 -0
  26. package/src/domains/creations/presentation/hooks/job-poller-utils.filters.ts +34 -0
  27. package/src/domains/creations/presentation/hooks/job-poller-utils.logger.ts +76 -0
  28. package/src/domains/creations/presentation/hooks/job-poller-utils.stale-handlers.ts +52 -0
  29. package/src/domains/creations/presentation/hooks/job-poller-utils.ts +8 -0
  30. package/src/domains/creations/presentation/hooks/useCreations.ts +1 -1
  31. package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +18 -235
  32. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +1 -2
  33. package/src/domains/creations/presentation-exports.ts +2 -2
  34. package/src/domains/face-detection/domain/entities/FaceDetection.ts +4 -3
  35. package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +24 -21
  36. package/src/domains/generation/infrastructure/appearance-analysis/index.ts +5 -0
  37. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-preparation.ts +58 -0
  38. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-prompt.ts +69 -0
  39. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-resolution.ts +77 -0
  40. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple.ts +54 -0
  41. package/src/domains/generation/infrastructure/couple-generation-builder/builder-index.ts +8 -0
  42. package/src/domains/generation/infrastructure/couple-generation-builder/builder-scenario.ts +112 -0
  43. package/src/domains/generation/infrastructure/couple-generation-builder/builder.ts +7 -0
  44. package/src/domains/generation/infrastructure/couple-generation-builder/index.ts +20 -0
  45. package/src/domains/generation/infrastructure/couple-generation-builder/types.ts +44 -0
  46. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-end-logger.ts +18 -0
  47. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-start-logger.ts +57 -0
  48. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-step-logger.ts +106 -0
  49. package/src/domains/generation/infrastructure/couple-generation-builder/utils/index.ts +8 -0
  50. package/src/domains/generation/infrastructure/couple-generation-builder/utils/types.ts +49 -0
  51. package/src/domains/generation/infrastructure/couple-generation-builder/utils.ts +8 -0
  52. package/src/domains/generation/infrastructure/flow/flow-store-actions.ts +105 -0
  53. package/src/domains/generation/infrastructure/flow/flow-store-initial-state.ts +26 -0
  54. package/src/domains/generation/infrastructure/flow/useFlowStore.ts +4 -116
  55. package/src/domains/generation/presentation/useAIGeneration.hook.ts +1 -1
  56. package/src/domains/generation/wizard/infrastructure/strategies/image-generation-strategy-index.ts +7 -0
  57. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts +2 -12
  58. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.types.ts +11 -0
  59. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.utils.ts +12 -0
  60. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts +1 -220
  61. package/src/domains/generation/wizard/infrastructure/strategies/image-input-builder.ts +66 -0
  62. package/src/domains/generation/wizard/infrastructure/strategies/image-input-extraction.ts +88 -0
  63. package/src/domains/generation/wizard/infrastructure/strategies/image-input-prompt-builder.ts +74 -0
  64. package/src/domains/generation/wizard/infrastructure/strategies/image-input-style-enhancements.ts +35 -0
  65. package/src/domains/generation/wizard/infrastructure/strategies/image-strategy-factory.ts +41 -0
  66. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor-index.ts +10 -0
  67. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor.ts +76 -0
  68. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-input-builder.ts +46 -0
  69. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-result-types.ts +17 -0
  70. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-submission.ts +61 -0
  71. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.audio-extractor.ts +27 -0
  72. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +2 -176
  73. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.input-builder.ts +90 -0
  74. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +3 -108
  75. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.types.ts +0 -130
  76. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.validation.ts +136 -0
  77. package/src/domains/generation/wizard/presentation/hooks/photo-upload/index.ts +40 -0
  78. package/src/domains/generation/wizard/presentation/hooks/photo-upload/types.ts +37 -0
  79. package/src/domains/generation/wizard/presentation/hooks/photo-upload/usePhotoUploadStateLogic.ts +142 -0
  80. package/src/domains/generation/wizard/presentation/hooks/use-video-queue-utils.ts +102 -0
  81. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.handlers.ts +97 -0
  82. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.saver.ts +54 -0
  83. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.ts +22 -87
  84. package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +8 -177
  85. package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +1 -295
  86. package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +1 -1
  87. package/src/domains/generation/wizard/presentation/hooks/video-queue/index.ts +77 -0
  88. package/src/domains/generation/wizard/presentation/hooks/video-queue/use-video-queue-utils.ts +123 -0
  89. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationCallbacks.ts +119 -0
  90. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationPolling.ts +75 -0
  91. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationRefs.ts +65 -0
  92. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationStart.ts +123 -0
  93. package/src/domains/generation/wizard/presentation/hooks/video-queue-index.ts +9 -0
  94. package/src/domains/image-to-video/domain/types/image-to-video-state.types.ts +11 -4
  95. package/src/domains/text-to-image/domain/constants/index.ts +5 -6
  96. package/src/domains/text-to-image/domain/types/text-to-image.types.ts +43 -22
  97. package/src/domains/text-to-video/domain/types/request.types.ts +32 -9
  98. package/src/domains/text-to-video/domain/types/state.types.ts +22 -22
  99. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.handlers.ts +44 -0
  100. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.ts +5 -51
  101. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.types.ts +33 -0
  102. package/src/exports/features.ts +1 -1
  103. package/src/infrastructure/services/generation-orchestrator.service.ts +2 -2
  104. package/src/infrastructure/utils/couple-input-context.ts +13 -0
  105. package/src/infrastructure/utils/couple-input-index.ts +9 -0
  106. package/src/infrastructure/utils/couple-input-photorealistic.ts +40 -0
  107. package/src/infrastructure/utils/couple-input-refiner.ts +101 -0
  108. package/src/infrastructure/utils/couple-input-resolver.ts +71 -0
  109. package/src/infrastructure/utils/couple-input-types.ts +11 -0
  110. package/src/infrastructure/utils/couple-input.util.ts +3 -176
  111. package/src/infrastructure/utils/photo-generation/photo-preparation.util.ts +1 -1
  112. package/src/infrastructure/validation/base-validator.ts +3 -26
  113. package/src/infrastructure/validation/base-validator.types.ts +32 -0
  114. package/src/presentation/hooks/generation/index.ts +1 -1
  115. package/src/presentation/hooks/generation/orchestrator-abort-logs.ts +48 -0
  116. package/src/presentation/hooks/generation/orchestrator-execution-logs.ts +67 -0
  117. package/src/presentation/hooks/generation/orchestrator-index.ts +14 -0
  118. package/src/presentation/hooks/generation/orchestrator-start-logs.ts +65 -0
  119. package/src/presentation/hooks/generation/orchestrator-state-utils.ts +17 -0
  120. package/src/presentation/hooks/generation/orchestrator-types.ts +55 -0
  121. package/src/presentation/hooks/generation/orchestrator-utils-index.ts +29 -0
  122. package/src/presentation/hooks/generation/orchestrator-utils.ts +25 -0
  123. package/src/presentation/hooks/generation/useDualImageGeneration.ts +1 -1
  124. package/src/presentation/hooks/generation/useImageGeneration.ts +1 -1
  125. package/src/presentation/hooks/generation/useVideoGeneration.ts +1 -1
  126. package/src/shared/hooks/factories/generation-hook-index.ts +12 -0
  127. package/src/shared/hooks/factories/generation-hook-types.ts +47 -0
  128. package/src/shared/hooks/factories/generation-hook-utils.ts +94 -0
  129. package/src/shared/hooks/factories/index.ts +1 -1
  130. package/src/shared/index.ts +1 -1
  131. package/src/shared/utils/calculations/aspect-ratio-calculations.ts +30 -0
  132. package/src/shared/utils/calculations/base64-calculations.ts +26 -0
  133. package/src/shared/utils/calculations/confidence-calculations.ts +21 -0
  134. package/src/shared/utils/calculations/cost-calculations-index.ts +43 -0
  135. package/src/shared/utils/calculations/cost-calculations.ts +25 -0
  136. package/src/shared/utils/calculations/credit-calculations.ts +37 -0
  137. package/src/shared/utils/calculations/index.ts +46 -0
  138. package/src/shared/utils/calculations/math-utilities.ts +32 -0
  139. package/src/shared/utils/calculations/memory-calculations.ts +33 -0
  140. package/src/shared/utils/calculations/pagination-calculations.ts +38 -0
  141. package/src/shared/utils/calculations/percentage-calculations.ts +33 -0
  142. package/src/shared/utils/calculations/time-calculations.ts +99 -0
  143. package/src/shared/utils/credit.ts +1 -1
  144. package/src/shared-kernel/application/hooks/index.ts +8 -0
  145. package/src/shared-kernel/application/hooks/use-feature-state.ts +106 -0
  146. package/src/shared-kernel/application/hooks/use-generation-handler.ts +110 -0
  147. package/src/shared-kernel/base-types/base-callbacks.types.ts +73 -0
  148. package/src/shared-kernel/base-types/base-feature-state.types.ts +77 -0
  149. package/src/shared-kernel/base-types/base-generation.types.ts +69 -0
  150. package/src/shared-kernel/base-types/index.ts +30 -0
  151. package/src/shared-kernel/domain/base-generation-strategy.ts +146 -0
  152. package/src/shared-kernel/domain/index.ts +7 -0
  153. package/src/shared-kernel/index.ts +17 -0
  154. package/src/shared-kernel/infrastructure/validation/common-validators.ts +126 -0
  155. package/src/shared-kernel/infrastructure/validation/common-validators.types.ts +33 -0
  156. package/src/shared-kernel/infrastructure/validation/error-handler.ts +52 -0
  157. package/src/shared-kernel/infrastructure/validation/error-handler.types.ts +38 -0
  158. package/src/shared-kernel/infrastructure/validation/error-handler.utils.ts +79 -0
  159. package/src/shared-kernel/infrastructure/validation/index.ts +70 -0
  160. package/src/domains/content-moderation/infrastructure/services/index.ts +0 -8
  161. package/src/domains/creations/domain/constants/index.ts +0 -12
  162. package/src/domains/creations/domain/utils/index.ts +0 -12
  163. package/src/domains/generation/infrastructure/couple-generation-builder.ts +0 -374
  164. package/src/domains/image-to-video/domain/index.ts +0 -2
  165. package/src/domains/image-to-video/infrastructure/index.ts +0 -1
  166. package/src/domains/image-to-video/presentation/index.ts +0 -5
  167. package/src/domains/text-to-video/domain/index.ts +0 -1
  168. package/src/domains/text-to-video/presentation/index.ts +0 -7
  169. package/src/presentation/hooks/generation/orchestrator.ts +0 -276
  170. package/src/shared/hooks/factories/createGenerationHook.ts +0 -253
  171. package/src/shared/utils/calculations.util.ts +0 -366
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.90.2",
3
+ "version": "1.90.4",
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",
@@ -73,8 +73,8 @@
73
73
  "@tanstack/react-query": "^5.66.7",
74
74
  "@tanstack/react-query-persist-client": "^5.66.7",
75
75
  "@types/react": "~19.1.10",
76
- "@typescript-eslint/eslint-plugin": "^8.54.0",
77
- "@typescript-eslint/parser": "^8.54.0",
76
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
77
+ "@typescript-eslint/parser": "^8.57.1",
78
78
  "@umituz/react-native-ai-fal-provider": "*",
79
79
  "@umituz/react-native-design-system": "*",
80
80
  "@umituz/react-native-firebase": "*",
@@ -0,0 +1,27 @@
1
+ /**
2
+ * App Services - Auth Interface
3
+ * Authentication service interface
4
+ */
5
+
6
+ /**
7
+ * Auth service interface
8
+ * Handles user authentication
9
+ */
10
+ export interface IAuthService {
11
+ /**
12
+ * Get current user ID
13
+ * @returns User ID or null if not authenticated
14
+ */
15
+ getUserId: () => string | null;
16
+
17
+ /**
18
+ * Check if user is authenticated
19
+ */
20
+ isAuthenticated: () => boolean;
21
+
22
+ /**
23
+ * Require authenticated user - throws if not authenticated
24
+ * @returns User ID
25
+ */
26
+ requireAuth: () => string;
27
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * App Services - Composite Interface
3
+ * Combined app services interface
4
+ */
5
+
6
+ import type { INetworkService } from "./app-services.interface";
7
+ import type { ICreditService } from "./app-services.interface";
8
+ import type { IPaywallService } from "./app-services.interface";
9
+ import type { IAuthService } from "./app-services-auth.interface";
10
+ import type { IAnalyticsService } from "./app-services-optional.interface";
11
+ import type { IFeatureUtils } from "./app-services-optional.interface";
12
+
13
+ /**
14
+ * Combined app services interface
15
+ * Apps implement this to provide all required services
16
+ */
17
+ export interface IAppServices {
18
+ readonly network: INetworkService;
19
+ readonly credits: ICreditService;
20
+ readonly paywall: IPaywallService;
21
+ readonly auth: IAuthService;
22
+ readonly analytics?: IAnalyticsService;
23
+ readonly featureUtils?: IFeatureUtils;
24
+ }
25
+
26
+ /**
27
+ * Partial app services for optional configuration
28
+ */
29
+ export type PartialAppServices = Partial<IAppServices>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * App Services - Optional Interfaces
3
+ * Optional service interfaces (analytics, feature utils)
4
+ */
5
+
6
+ /**
7
+ * Analytics event data
8
+ */
9
+ interface AnalyticsEventData {
10
+ readonly [key: string]: string | number | boolean | null | undefined;
11
+ }
12
+
13
+ /**
14
+ * Analytics service interface (optional)
15
+ * Tracks events for analytics
16
+ */
17
+ export interface IAnalyticsService {
18
+ /**
19
+ * Track an event
20
+ * @param event - Event name
21
+ * @param data - Event data
22
+ */
23
+ track: (event: string, data: AnalyticsEventData) => void;
24
+ }
25
+
26
+ /**
27
+ * Feature utils interface (optional)
28
+ * Provides utility functions for AI features
29
+ */
30
+ export interface IFeatureUtils {
31
+ /**
32
+ * Select image from gallery
33
+ * @returns Image URI or null if cancelled
34
+ */
35
+ selectImage: () => Promise<string | null>;
36
+
37
+ /**
38
+ * Save video to device library
39
+ * @param uri - Video URI to save
40
+ */
41
+ saveVideo: (uri: string) => Promise<void>;
42
+ }
@@ -78,82 +78,3 @@ export interface IPaywallService {
78
78
  */
79
79
  showPaywall: (requiredCredits: number) => void;
80
80
  }
81
-
82
- /**
83
- * Auth service interface
84
- * Handles user authentication
85
- */
86
- export interface IAuthService {
87
- /**
88
- * Get current user ID
89
- * @returns User ID or null if not authenticated
90
- */
91
- getUserId: () => string | null;
92
-
93
- /**
94
- * Check if user is authenticated
95
- */
96
- isAuthenticated: () => boolean;
97
-
98
- /**
99
- * Require authenticated user - throws if not authenticated
100
- * @returns User ID
101
- */
102
- requireAuth: () => string;
103
- }
104
-
105
- /**
106
- * Analytics event data
107
- */
108
- interface AnalyticsEventData {
109
- readonly [key: string]: string | number | boolean | null | undefined;
110
- }
111
-
112
- /**
113
- * Analytics service interface (optional)
114
- * Tracks events for analytics
115
- */
116
- export interface IAnalyticsService {
117
- /**
118
- * Track an event
119
- * @param event - Event name
120
- * @param data - Event data
121
- */
122
- track: (event: string, data: AnalyticsEventData) => void;
123
- }
124
-
125
- /**
126
- * Feature utils interface (optional)
127
- * Provides utility functions for AI features
128
- */
129
- export interface IFeatureUtils {
130
- /**
131
- * Select image from gallery
132
- * @returns Image URI or null if cancelled
133
- */
134
- selectImage: () => Promise<string | null>;
135
-
136
- /**
137
- * Save video to device library
138
- * @param uri - Video URI to save
139
- */
140
- saveVideo: (uri: string) => Promise<void>;
141
- }
142
-
143
- /**
144
- * Combined app services interface
145
- * Apps implement this to provide all required services
146
- */
147
- export interface IAppServices {
148
- readonly network: INetworkService;
149
- readonly credits: ICreditService;
150
- readonly paywall: IPaywallService;
151
- readonly auth: IAuthService;
152
- readonly analytics?: IAnalyticsService;
153
- readonly featureUtils?: IFeatureUtils;
154
- }
155
-
156
- /**
157
- * Partial app services for optional configuration
158
- */
159
- export type PartialAppServices = Partial<IAppServices>;
@@ -5,6 +5,9 @@
5
5
 
6
6
  export * from "./ai-provider.interface";
7
7
  export * from "./app-services.interface";
8
+ export * from "./app-services-auth.interface";
9
+ export * from "./app-services-composite.interface";
10
+ export * from "./app-services-optional.interface";
8
11
 
9
12
  // Video Model Configuration
10
13
  export type { VideoModelConfig, ModelCapabilityOption } from "./video-model-config.types";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Job Poller Service
3
+ * Provider-agnostic job polling with exponential backoff
4
+ */
5
+
6
+ export { pollJob } from "./job-poller.service";
7
+ export type { PollJobOptions, PollJobResult } from "./job-poller.service";
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Job Poller Service - Utility Functions
3
+ */
4
+
5
+ /**
6
+ * Wrap a promise with abort signal support
7
+ * Rejects if signal is aborted before promise resolves
8
+ */
9
+ export function withAbortSignal<T>(
10
+ promise: Promise<T>,
11
+ signal: AbortSignal | undefined,
12
+ timeoutMs?: number,
13
+ ): Promise<T> {
14
+ if (!signal && !timeoutMs) {
15
+ return promise;
16
+ }
17
+
18
+ return new Promise<T>((resolve, reject) => {
19
+ // Handle abort signal
20
+ if (signal?.aborted) {
21
+ reject(new Error("Operation aborted"));
22
+ return;
23
+ }
24
+
25
+ let isResolved = false;
26
+ const abortHandler = () => {
27
+ if (isResolved) return;
28
+ isResolved = true;
29
+ if (timeoutId) clearTimeout(timeoutId);
30
+ reject(new Error("Operation aborted"));
31
+ };
32
+
33
+ signal?.addEventListener("abort", abortHandler, { once: true });
34
+
35
+ // Handle timeout
36
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
37
+ if (timeoutMs) {
38
+ timeoutId = setTimeout(() => {
39
+ if (isResolved) return;
40
+ isResolved = true;
41
+ signal?.removeEventListener("abort", abortHandler);
42
+ reject(new Error(`Operation timeout after ${timeoutMs}ms`));
43
+ }, timeoutMs);
44
+ }
45
+
46
+ promise
47
+ .then((result) => {
48
+ if (isResolved) return;
49
+ isResolved = true;
50
+ signal?.removeEventListener("abort", abortHandler);
51
+ if (timeoutId) clearTimeout(timeoutId);
52
+ resolve(result);
53
+ })
54
+ .catch((error) => {
55
+ if (isResolved) return;
56
+ isResolved = true;
57
+ signal?.removeEventListener("abort", abortHandler);
58
+ if (timeoutId) clearTimeout(timeoutId);
59
+ reject(error);
60
+ });
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Validate request ID
66
+ */
67
+ export function validateRequestId(requestId: string | undefined): { valid: boolean; error?: string } {
68
+ if (!requestId || typeof requestId !== "string" || requestId.trim() === "") {
69
+ return {
70
+ valid: false,
71
+ error: "Invalid requestId provided",
72
+ };
73
+ }
74
+ return { valid: true };
75
+ }
76
+
77
+ /**
78
+ * Validate model
79
+ */
80
+ export function validateModel(model: string | undefined): { valid: boolean; error?: string } {
81
+ if (!model || typeof model !== "string" || model.trim() === "") {
82
+ return {
83
+ valid: false,
84
+ error: "Invalid model provided",
85
+ };
86
+ }
87
+ return { valid: true };
88
+ }
89
+
90
+ /**
91
+ * Log transient error during polling
92
+ */
93
+ export function logTransientError(
94
+ attempt: number,
95
+ requestId: string,
96
+ model: string,
97
+ consecutiveErrors: number,
98
+ error: unknown
99
+ ): void {
100
+ if (__DEV__) {
101
+ console.warn("[JobPoller] Transient error during polling", {
102
+ attempt: attempt + 1,
103
+ requestId,
104
+ model,
105
+ consecutiveErrors,
106
+ error: error instanceof Error ? error.message : String(error),
107
+ code: (error as { code?: string })?.code,
108
+ });
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Log max consecutive errors reached
114
+ */
115
+ export function logMaxConsecutiveErrors(
116
+ maxConsecutiveErrors: number,
117
+ requestId: string,
118
+ model: string
119
+ ): void {
120
+ if (__DEV__) {
121
+ console.error("[JobPoller] Max consecutive errors reached", {
122
+ maxConsecutiveErrors,
123
+ requestId,
124
+ model,
125
+ });
126
+ }
127
+ }
@@ -1,234 +1,179 @@
1
1
  /**
2
2
  * Job Poller Service
3
3
  * Provider-agnostic job polling with exponential backoff
4
- * Reports only real status - no fake progress
5
4
  */
6
5
 
7
- import { DEFAULT_POLLING_CONFIG } from "../../../../domain/entities/polling.types";
8
- import { calculatePollingInterval } from "../utils/polling-interval.util";
6
+ import type { JobStatus } from "../../../../domain/interfaces/ai-provider.interface";
7
+ import type { PollingConfig } from "../../../../domain/entities/polling.types";
8
+ import type { PollJobOptions, PollJobResult } from "./job-poller.types";
9
+ import { withAbortSignal, validateRequestId, validateModel, logTransientError, logMaxConsecutiveErrors } from "./job-poller-utils";
9
10
  import { checkStatusForErrors, isJobComplete } from "../utils/status-checker.util";
10
11
  import { validateResult } from "../utils/result-validator.util";
11
- import type { PollJobOptions, PollJobResult } from "./job-poller.types";
12
-
13
12
 
14
13
  /**
15
- * Wraps a promise with abort signal support
16
- * Rejects if signal is aborted before promise resolves
14
+ * Default polling configuration
17
15
  */
18
- function withAbortSignal<T>(
19
- promise: Promise<T>,
20
- signal: AbortSignal | undefined,
21
- timeoutMs?: number,
22
- ): Promise<T> {
23
- if (!signal && !timeoutMs) {
24
- return promise;
25
- }
26
-
27
- return new Promise<T>((resolve, reject) => {
28
- // Handle abort signal
29
- if (signal?.aborted) {
30
- reject(new Error("Operation aborted"));
31
- return;
32
- }
33
-
34
- let isResolved = false;
35
- const abortHandler = () => {
36
- if (isResolved) return;
37
- isResolved = true;
38
- if (timeoutId) clearTimeout(timeoutId);
39
- reject(new Error("Operation aborted"));
40
- };
41
-
42
- signal?.addEventListener("abort", abortHandler, { once: true });
43
-
44
- // Handle timeout
45
- let timeoutId: ReturnType<typeof setTimeout> | undefined;
46
- if (timeoutMs) {
47
- timeoutId = setTimeout(() => {
48
- if (isResolved) return;
49
- isResolved = true;
50
- signal?.removeEventListener("abort", abortHandler);
51
- reject(new Error(`Operation timeout after ${timeoutMs}ms`));
52
- }, timeoutMs);
53
- }
54
-
55
- promise
56
- .then((result) => {
57
- if (isResolved) return;
58
- isResolved = true;
59
- signal?.removeEventListener("abort", abortHandler);
60
- if (timeoutId) clearTimeout(timeoutId);
61
- resolve(result);
62
- })
63
- .catch((error) => {
64
- if (isResolved) return;
65
- isResolved = true;
66
- signal?.removeEventListener("abort", abortHandler);
67
- if (timeoutId) clearTimeout(timeoutId);
68
- reject(error);
69
- });
70
- });
71
- }
16
+ const DEFAULT_CONFIG: Partial<PollingConfig> = {
17
+ maxAttempts: 40,
18
+ initialIntervalMs: 1500,
19
+ maxIntervalMs: 5000,
20
+ backoffMultiplier: 1.3,
21
+ maxConsecutiveErrors: 5,
22
+ };
72
23
 
73
24
  /**
74
- * Poll job until completion with exponential backoff
75
- * Only reports 100% on actual completion
25
+ * Poll a job until completion or timeout
76
26
  */
77
27
  export async function pollJob<T = unknown>(
78
- options: PollJobOptions,
28
+ options: PollJobOptions
79
29
  ): Promise<PollJobResult<T>> {
80
30
  const {
81
31
  provider,
82
32
  model,
83
33
  requestId,
84
- config,
34
+ config = DEFAULT_CONFIG,
85
35
  onProgress,
86
36
  onStatusChange,
87
37
  signal,
88
38
  } = options;
89
39
 
90
- // Validate requestId early
91
- if (!requestId || typeof requestId !== "string" || requestId.trim() === "") {
40
+ // Validate inputs
41
+ const requestIdValidation = validateRequestId(requestId);
42
+ if (!requestIdValidation.valid) {
92
43
  return {
93
44
  success: false,
94
- error: new Error("Invalid requestId provided"),
45
+ error: new Error(requestIdValidation.error!),
95
46
  attempts: 0,
96
47
  elapsedMs: 0,
97
48
  };
98
49
  }
99
50
 
100
- // Validate model early
101
- if (!model || typeof model !== "string" || model.trim() === "") {
51
+ const modelValidation = validateModel(model);
52
+ if (!modelValidation.valid) {
102
53
  return {
103
54
  success: false,
104
- error: new Error("Invalid model provided"),
55
+ error: new Error(modelValidation.error!),
105
56
  attempts: 0,
106
57
  elapsedMs: 0,
107
58
  };
108
59
  }
109
60
 
110
- const pollingConfig = { ...DEFAULT_POLLING_CONFIG, ...config };
111
- const { maxAttempts, maxTotalTimeMs, maxConsecutiveErrors } = pollingConfig;
112
-
113
61
  const startTime = Date.now();
114
- let consecutiveTransientErrors = 0;
62
+ let currentInterval = config.initialIntervalMs || DEFAULT_CONFIG.initialIntervalMs!;
63
+ const maxInterval = config.maxIntervalMs || DEFAULT_CONFIG.maxIntervalMs!;
64
+ const maxAttempts = config.maxAttempts || DEFAULT_CONFIG.maxAttempts!;
65
+ const backoffMultiplier = config.backoffMultiplier || DEFAULT_CONFIG.backoffMultiplier!;
66
+ const maxConsecutiveErrors = config.maxConsecutiveErrors || DEFAULT_CONFIG.maxConsecutiveErrors!;
115
67
 
116
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
117
- // Check total time limit
118
- const elapsedMs = Date.now() - startTime;
119
- if (maxTotalTimeMs && elapsedMs >= maxTotalTimeMs) {
120
- return {
121
- success: false,
122
- error: new Error(`Polling timeout after ${elapsedMs}ms`),
123
- attempts: attempt + 1,
124
- elapsedMs,
125
- };
126
- }
68
+ let consecutiveErrors = 0;
127
69
 
70
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
71
+ // Check for abort signal
128
72
  if (signal?.aborted) {
129
73
  return {
130
74
  success: false,
131
- error: new Error("Polling aborted"),
75
+ error: new Error("Operation aborted"),
132
76
  attempts: attempt + 1,
133
- elapsedMs,
77
+ elapsedMs: Date.now() - startTime,
134
78
  };
135
79
  }
136
80
 
137
- if (attempt > 0) {
138
- const interval = calculatePollingInterval({ attempt, config: pollingConfig });
139
- await new Promise<void>((resolve) => setTimeout(() => resolve(), interval));
140
- }
141
-
142
81
  try {
143
- // Wrap provider calls with abort signal support and timeout (30s default)
144
- const status = await withAbortSignal(
145
- provider.getJobStatus(model, requestId),
82
+ // Check job status
83
+ const statusResult = await withAbortSignal(
84
+ provider.checkStatus(requestId, model),
146
85
  signal,
147
- 30000,
86
+ config.maxTotalTimeMs ? config.maxTotalTimeMs - (Date.now() - startTime) : undefined
148
87
  );
149
- onStatusChange?.(status);
150
88
 
151
- const statusCheck = checkStatusForErrors(status);
89
+ // Notify status change
90
+ if (onStatusChange && typeof statusResult === 'object' && 'status' in statusResult) {
91
+ onStatusChange(statusResult as JobStatus);
92
+ }
152
93
 
153
- if (statusCheck.shouldStop && statusCheck.hasError) {
94
+ // Check for errors in status
95
+ const statusError = checkStatusForErrors(statusResult as JobStatus | Record<string, unknown>);
96
+ if (statusError) {
154
97
  return {
155
98
  success: false,
156
- error: new Error(statusCheck.errorMessage || "Job failed"),
99
+ error: statusError,
157
100
  attempts: attempt + 1,
158
101
  elapsedMs: Date.now() - startTime,
159
102
  };
160
103
  }
161
104
 
162
- consecutiveTransientErrors = 0;
105
+ // Reset consecutive errors on success
106
+ consecutiveErrors = 0;
163
107
 
164
- if (isJobComplete(status)) {
165
- // Wrap result retrieval with abort signal support and timeout (60s for larger results)
166
- const result = await withAbortSignal(
167
- provider.getJobResult<T>(model, requestId),
168
- signal,
169
- 60000,
170
- );
171
-
172
- const validation = validateResult(result);
173
- if (!validation.isValid) {
108
+ // Check if job is complete
109
+ if (isJobComplete(statusResult as JobStatus | Record<string, unknown>)) {
110
+ // Validate result
111
+ const validationResult = validateResult(statusResult);
112
+ if (!validationResult.isValid) {
174
113
  return {
175
114
  success: false,
176
- error: new Error(validation.errorMessage || "Invalid result"),
115
+ error: new Error(validationResult.errorMessage || "Result validation failed"),
177
116
  attempts: attempt + 1,
178
117
  elapsedMs: Date.now() - startTime,
179
118
  };
180
119
  }
181
120
 
121
+ // Report progress
182
122
  onProgress?.(100);
183
123
 
124
+ // Extract result data if available
125
+ let data: T | undefined = undefined;
126
+ if (typeof statusResult === 'object' && statusResult !== null && 'result' in statusResult) {
127
+ data = statusResult.result as T;
128
+ } else {
129
+ data = statusResult as T;
130
+ }
131
+
184
132
  return {
185
133
  success: true,
186
- data: result,
134
+ data,
187
135
  attempts: attempt + 1,
188
136
  elapsedMs: Date.now() - startTime,
189
137
  };
190
138
  }
139
+
140
+ // Report progress based on attempt number
141
+ const progress = Math.min((attempt / maxAttempts) * 100, 90);
142
+ onProgress?.(progress);
143
+
191
144
  } catch (error) {
192
- consecutiveTransientErrors++;
193
-
194
- if (__DEV__) {
195
- console.warn("[JobPoller] Transient error during polling", {
196
- attempt: attempt + 1,
197
- requestId,
198
- model,
199
- consecutiveErrors: consecutiveTransientErrors,
200
- error: error instanceof Error ? error.message : String(error),
201
- code: (error as { code?: string })?.code,
202
- });
203
- }
145
+ consecutiveErrors++;
146
+ logTransientError(attempt, requestId, model, consecutiveErrors, error);
204
147
 
205
- // Check if we've hit max consecutive transient errors
206
- if (maxConsecutiveErrors && consecutiveTransientErrors >= maxConsecutiveErrors) {
207
- if (__DEV__) {
208
- console.error("[JobPoller] Max consecutive errors reached", {
209
- maxConsecutiveErrors,
210
- requestId,
211
- model,
212
- });
213
- }
148
+ // Check if we've hit max consecutive errors
149
+ if (consecutiveErrors >= maxConsecutiveErrors) {
150
+ logMaxConsecutiveErrors(maxConsecutiveErrors, requestId, model);
214
151
  return {
215
152
  success: false,
216
- error: new Error(`Too many consecutive errors (${consecutiveTransientErrors})`),
153
+ error: error instanceof Error ? error : new Error(String(error)),
217
154
  attempts: attempt + 1,
218
155
  elapsedMs: Date.now() - startTime,
219
156
  };
220
157
  }
221
-
222
- // Continue polling on transient errors
223
- continue;
224
158
  }
159
+
160
+ // Wait before next attempt with exponential backoff
161
+ await withAbortSignal(
162
+ new Promise(resolve => setTimeout(resolve, currentInterval)),
163
+ signal
164
+ );
165
+
166
+ // Increase interval for next attempt (exponential backoff)
167
+ currentInterval = Math.min(currentInterval * backoffMultiplier, maxInterval);
225
168
  }
226
169
 
170
+ // Max attempts reached
227
171
  return {
228
172
  success: false,
229
- error: new Error(`Polling timeout after ${maxAttempts} attempts`),
173
+ error: new Error(`Job did not complete after ${maxAttempts} attempts`),
230
174
  attempts: maxAttempts,
231
175
  elapsedMs: Date.now() - startTime,
232
176
  };
233
177
  }
234
178
 
179
+ export type { PollJobOptions, PollJobResult } from './job-poller.types';
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { PollingConfig } from "../../../../domain/entities/polling.types";
7
- import { calculatePollingInterval as calculateInterval } from "../../../../shared/utils/calculations.util";
7
+ import { calculatePollingInterval as calculateInterval } from "../../../../shared/utils/calculations";
8
8
 
9
9
  export interface IntervalOptions {
10
10
  attempt: number;
@@ -2,7 +2,7 @@ import { useCallback, useRef, useState, useMemo } from "react";
2
2
  import { usePendingJobs } from "./use-pending-jobs";
3
3
  import { executeDirectGeneration, executeQueuedJob } from "../../infrastructure/executors/backgroundJobExecutor";
4
4
  import { DEFAULT_QUEUE_CONFIG } from "../../domain/entities/job.types";
5
- import { calculateFilteredCount } from "../../../../shared/utils/calculations.util";
5
+ import { calculateFilteredCount } from "../../../../shared/utils/calculations";
6
6
  import type {
7
7
  UseBackgroundGenerationOptions,
8
8
  UseBackgroundGenerationReturn,