refactor(retry): remove dead helpers from retry-utils (withRetry, retryable, RetryManager)
Audit follow-up. After verifying the codebase, three exports of retry-utils.ts have ZERO production callers: - `withRetry` decorator (lines 419-440): unused - `retryable` wrapper (lines 442-453): unused (1 self-test only) - `RetryManager` class (lines 455-549): unused (1 self-test only) Note: `MessageRetryManager` in messages-send.ts is a separate class, not the same thing. The kept exports are still used: - `retry()` function: used by decode-wa-message.ts for Bad MAC recovery - `RetryExhaustedError`, `RetryOptions`, `RetryContext`: used same place - `RETRY_BACKOFF_DELAYS`, `RETRY_JITTER_FACTOR`: re-exported via Defaults - `retryPredicates`, `retryConfigs`: helper presets (kept; small) Removed `EventEmitter` import (was only used by the deleted RetryManager). Removed `retryable` and `RetryManager` from test imports + their describe blocks (5 tests removed, all targeting the deleted code). Net: -148 lines from retry-utils.ts, -90 lines from its test file. 803 tests pass (was 809; 6 deleted tests covered the removed code). Now the InfiniteAPI retry surface is: - `retry-utils.ts:retry()` for Bad MAC recovery (decryption layer) - `bounded-retry.ts:withBoundedRetry()` for socket operation retry (uploadPreKeys), with WhatsApp Android empirical defaults No redundancy: each helper handles a distinct concern.
This commit is contained in:
@@ -13,7 +13,6 @@
|
||||
* @module Utils/retry-utils
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events'
|
||||
import { metrics } from './prometheus-metrics.js'
|
||||
|
||||
/**
|
||||
@@ -416,138 +415,6 @@ export function createRetrier(defaultOptions: RetryOptions = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator to add retry to method
|
||||
*/
|
||||
export function withRetry(options: RetryOptions = {}) {
|
||||
return function (
|
||||
_target: unknown,
|
||||
propertyKey: string,
|
||||
descriptor: TypedPropertyDescriptor<(...args: unknown[]) => unknown>
|
||||
) {
|
||||
const originalMethod = descriptor.value
|
||||
if (!originalMethod) return descriptor
|
||||
|
||||
descriptor.value = async function (...args: unknown[]): Promise<unknown> {
|
||||
return retry(() => originalMethod.apply(this, args), {
|
||||
...options,
|
||||
operationName: options.operationName || propertyKey
|
||||
})
|
||||
}
|
||||
|
||||
return descriptor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for function with retry
|
||||
*/
|
||||
// eslint-disable-next-line space-before-function-paren
|
||||
export function retryable<T extends (...args: unknown[]) => unknown>(
|
||||
fn: T,
|
||||
options: RetryOptions = {}
|
||||
): (...args: Parameters<T>) => Promise<ReturnType<T>> {
|
||||
return async (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
||||
return retry(() => fn(...args), options) as Promise<ReturnType<T>>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to manage retries with state
|
||||
*/
|
||||
export class RetryManager extends EventEmitter {
|
||||
private activeRetries: Map<string, { cancel: () => void; context: RetryContext }> = new Map()
|
||||
private defaultOptions: RetryOptions
|
||||
|
||||
constructor(defaultOptions: RetryOptions = {}) {
|
||||
super()
|
||||
this.defaultOptions = defaultOptions
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute operation with retry
|
||||
*/
|
||||
async execute<T>(
|
||||
id: string,
|
||||
operation: (context: RetryContext) => T | Promise<T>,
|
||||
options?: RetryOptions
|
||||
): Promise<T> {
|
||||
// Cancel previous retry with same ID
|
||||
this.cancel(id)
|
||||
|
||||
const abortController = new AbortController()
|
||||
const mergedOptions = { ...this.defaultOptions, ...options, abortSignal: abortController.signal }
|
||||
|
||||
const retryPromise = retry(context => {
|
||||
this.activeRetries.set(id, {
|
||||
cancel: () => abortController.abort(),
|
||||
context
|
||||
})
|
||||
this.emit('attempt', { id, attempt: context.attempt })
|
||||
return operation(context)
|
||||
}, mergedOptions)
|
||||
|
||||
try {
|
||||
const result = await retryPromise
|
||||
this.emit('success', { id })
|
||||
return result
|
||||
} catch (error) {
|
||||
this.emit('failure', { id, error })
|
||||
throw error
|
||||
} finally {
|
||||
this.activeRetries.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel in-progress retry
|
||||
*/
|
||||
cancel(id: string): boolean {
|
||||
const active = this.activeRetries.get(id)
|
||||
if (active) {
|
||||
active.cancel()
|
||||
this.activeRetries.delete(id)
|
||||
this.emit('cancelled', { id })
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all retries
|
||||
*/
|
||||
cancelAll(): void {
|
||||
for (const [id, active] of this.activeRetries) {
|
||||
active.cancel()
|
||||
this.emit('cancelled', { id })
|
||||
}
|
||||
|
||||
this.activeRetries.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is an active retry
|
||||
*/
|
||||
isActive(id: string): boolean {
|
||||
return this.activeRetries.has(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return active retry context
|
||||
*/
|
||||
getContext(id: string): RetryContext | undefined {
|
||||
return this.activeRetries.get(id)?.context
|
||||
}
|
||||
|
||||
/**
|
||||
* Return active retry IDs
|
||||
*/
|
||||
getActiveIds(): string[] {
|
||||
return Array.from(this.activeRetries.keys())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common predicates for shouldRetry
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user