Nicolas Cantu 3000872dbc refactoring
- **Motivations :** Assurer passage du lint strict et clarifier la logique paiements/publications.

- **Root causes :** Fonctions trop longues, promesses non gérées et typages WebLN/Nostr incomplets.

- **Correctifs :** Refactor PaymentModal (handlers void), extraction helpers articlePublisher, simplification polling sponsoring/zap, corrections curly et awaits.

- **Evolutions :** Nouveau module articlePublisherHelpers pour présentation/aiguillage contenu privé.

- **Page affectées :** components/PaymentModal.tsx, lib/articlePublisher.ts, lib/articlePublisherHelpers.ts, lib/paymentPolling.ts, lib/sponsoring.ts, lib/nostrZapVerification.ts et dépendances liées.
2025-12-22 17:56:00 +01:00

81 lines
1.9 KiB
TypeScript

/**
* Retry utility with exponential backoff
*/
export interface RetryOptions {
maxRetries?: number
initialDelay?: number
maxDelay?: number
backoffMultiplier?: number
retryable?: (error: Error) => boolean
}
const DEFAULT_OPTIONS: Required<RetryOptions> = {
maxRetries: 3,
initialDelay: 1000,
maxDelay: 10000,
backoffMultiplier: 2,
retryable: () => true,
}
/**
* Retry a function with exponential backoff
*/
export function retryWithBackoff<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {
const opts = { ...DEFAULT_OPTIONS, ...options }
const attempt = (current: number): Promise<T> => {
return fn().catch((error) => {
const normalizedError = error instanceof Error ? error : new Error(String(error))
if (!opts.retryable(normalizedError) || current === opts.maxRetries) {
throw normalizedError
}
const delay = Math.min(opts.initialDelay * Math.pow(opts.backoffMultiplier, current), opts.maxDelay)
return new Promise((resolve) => {
setTimeout(() => {
resolve(attempt(current + 1))
}, delay)
})
})
}
return attempt(0).catch((error) => {
throw error ?? new Error('Retry failed')
})
}
/**
* Check if an error is a network error that should be retried
*/
export function isRetryableNetworkError(error: Error): boolean {
// Network errors
if (error.message.includes('network') || error.message.includes('fetch')) {
return true
}
// Timeout errors
if (error.message.includes('timeout') || error.message.includes('timed out')) {
return true
}
// Connection errors
if (error.message.includes('ECONNRESET') || error.message.includes('ECONNREFUSED')) {
return true
}
// Rate limiting (429)
if (error.message.includes('429') || error.message.includes('rate limit')) {
return true
}
// Server errors (5xx)
if (error.message.includes('50')) {
return true
}
return false
}