@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.
- package/package.json +1 -1
- package/src/domain/entities/error.types.ts +0 -1
- package/src/domain/entities/job.types.ts +0 -4
- package/src/domain/entities/polling.types.ts +1 -3
- package/src/domain/interfaces/app-services.interface.ts +20 -2
- package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +2 -1
- package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +84 -4
- package/src/domains/content-moderation/infrastructure/services/pattern-matcher.service.ts +85 -2
- package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +102 -19
- package/src/domains/creations/presentation/hooks/useAdvancedFilter.ts +13 -4
- package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +10 -9
- package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +1 -1
- package/src/domains/generation/infrastructure/flow/useFlow.ts +11 -6
- package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +2 -3
- package/src/exports/infrastructure.ts +24 -1
- package/src/exports/presentation.ts +1 -1
- package/src/features/image-to-video/presentation/components/index.ts +0 -4
- package/src/index.ts +0 -4
- package/src/infrastructure/constants/index.ts +14 -2
- package/src/infrastructure/constants/polling.constants.ts +34 -0
- package/src/infrastructure/constants/storage.constants.ts +25 -0
- package/src/infrastructure/constants/validation.constants.ts +40 -0
- package/src/infrastructure/logging/logger.ts +185 -0
- package/src/infrastructure/services/job-poller.service.ts +13 -28
- package/src/infrastructure/utils/status-checker.util.ts +27 -7
- package/src/infrastructure/validation/input-validator.ts +406 -0
- package/src/presentation/components/AIGenerationForm.tsx +0 -11
- package/src/presentation/components/ErrorBoundary.tsx +141 -0
- package/src/presentation/components/index.ts +1 -0
- package/src/presentation/hooks/use-background-generation.ts +0 -12
- package/src/presentation/hooks/useAIFeatureCallbacks.ts +3 -4
- 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(),
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
23
|
-
|
|
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
|
|
29
|
-
(status
|
|
30
|
-
(status
|
|
31
|
-
const hasStatusError =
|
|
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;
|