export const TRY_ON_ERROR_CODES = ['photo_body_hidden', 'content_refused', 'generation_refused', 'quality_failed', 'bad_image'] as const; export type TryOnErrorCode = typeof TRY_ON_ERROR_CODES[number]; export function isTryOnErrorCode(value: unknown): value is TryOnErrorCode { return typeof value === 'string' && (TRY_ON_ERROR_CODES as readonly string[]).includes(value); } export class TryOnRejectedError extends Error { readonly retryable: boolean; constructor(public readonly code: TryOnErrorCode, retryable?: boolean) { super(code); this.name = 'TryOnRejectedError'; this.retryable = code === 'quality_failed' && retryable !== false; } } type MessageKey = 'photoBodyHidden' | 'refused' | 'qualityFailed' | 'unreadableImage' | 'network' | 'imageLoad' | 'timeout' | 'server' | 'processingFailed' | 'unexpected'; export function classifyTryOnError(error: unknown): { messageKey: MessageKey; retryable: boolean; photoProblem: boolean } { if (error instanceof TryOnRejectedError) { const keys = { photo_body_hidden: 'photoBodyHidden', content_refused: 'refused', generation_refused: 'refused', quality_failed: 'qualityFailed', bad_image: 'unreadableImage' } as const; return { messageKey: keys[error.code], retryable: error.retryable, photoProblem: error.code === 'photo_body_hidden' || error.code === 'bad_image' }; } const message = error instanceof Error ? error.message.toLowerCase() : ''; let messageKey: MessageKey = 'unexpected'; if (message.includes('refused') || message.includes('safety')) messageKey = 'refused'; else if (message.includes('failed to fetch image') || message.includes('image load') || message.includes('could not load image')) messageKey = 'imageLoad'; else if (message.includes('failed to fetch') || message.includes('network') || message.includes('cors')) messageKey = 'network'; else if (message.includes('timed out') || message.includes('timeout')) messageKey = 'timeout'; else if (message.includes('api error') || message.includes('401') || message.includes('403')) messageKey = 'server'; else if (message.includes('job failed')) messageKey = 'processingFailed'; return { messageKey, retryable: messageKey !== 'refused', photoProblem: false }; }