npm package: @srijankarak/retry-fn
Description: Lightweight utility for retrying async functions with configurable backoff strategies
Author: Srijan Karak
Latest version: 1.0.0

# retry-fn

A lightweight TypeScript utility for retrying async functions with configurable backoff strategies.

[![npm version](https://badge.fury.io/js/retry-fn.svg)](https://www.npmjs.com/package/retry-fn)

## Features

- 🔄 **Automatic retries** with configurable attempts
- ⏱️ **Multiple backoff strategies**: fixed, linear, exponential, or custom
- 🛑 **Abort support** via AbortController
- 🎯 **Conditional retries** with `retryIf`
- 📢 **Retry callbacks** for logging/monitoring
- 📦 **Zero dependencies**
- 💪 **Full TypeScript support**

## Installation

```bash
npm install retry-fn
```

## Quick Start

```typescript
import { retry } from 'retry-fn';

// Basic usage - retry up to 3 times with exponential backoff
const data = await retry(() => fetch('/api/data').then(r => r.json()));

// With options
const result = await retry(
  () => fetchUserData(userId),
  {
    retries: 5,
    delay: 1000,
    backoff: 'exponential',
  }
);
```

## API

### `retry<T>(fn, options?): Promise<T>`

Retry an async function until it succeeds or retries are exhausted.

#### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `fn` | `() => Promise<T>` | Async function to retry |
| `options` | `RetryOptions` | Configuration options |

#### Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `retries` | `number` | `3` | Maximum number of retry attempts |
| `delay` | `number` | `1000` | Initial delay in milliseconds |
| `backoff` | `BackoffStrategy` | `'exponential'` | Backoff strategy |
| `maxDelay` | `number` | `30000` | Maximum delay cap in ms |
| `factor` | `number` | `2` | Multiplier for backoff |
| `onRetry` | `function` | - | Callback on each retry |
| `retryIf` | `function` | - | Condition to determine if retry should occur |
| `signal` | `AbortSignal` | - | AbortSignal for cancellation |

## Examples

### Exponential Backoff

```typescript
const result = await retry(fetchData, {
  retries: 5,
  delay: 1000,
  backoff: 'exponential',
  factor: 2,
});
// Delays: 1s, 2s, 4s, 8s, 16s
```

### Linear Backoff

```typescript
const result = await retry(fetchData, {
  retries: 5,
  delay: 1000,
  backoff: 'linear',
  factor: 1,
});
// Delays: 1s, 2s, 3s, 4s, 5s
```

### Fixed Delay

```typescript
const result = await retry(fetchData, {
  retries: 3,
  delay: 2000,
  backoff: 'fixed',
});
// Delays: 2s, 2s, 2s
```

### Custom Backoff Function

```typescript
const result = await retry(fetchData, {
  retries: 5,
  delay: 1000,
  backoff: (attempt, delay, factor) => {
    // Custom: random jitter
    const jitter = Math.random() * 500;
    return delay * Math.pow(factor, attempt - 1) + jitter;
  },
});
```

### Retry Callback (Logging)

```typescript
const result = await retry(fetchData, {
  retries: 3,
  onRetry: (error, attempt) => {
    console.log(`Attempt ${attempt} failed:`, error.message);
  },
});
```

### Conditional Retry

```typescript
const result = await retry(fetchData, {
  retries: 3,
  retryIf: (error) => {
    // Only retry on network errors, not 4xx responses
    return error.name === 'NetworkError';
  },
});
```

### Abort / Cancel

```typescript
const controller = new AbortController();

// Start retry
const resultPromise = retry(fetchData, {
  retries: 10,
  delay: 5000,
  signal: controller.signal,
});

// Cancel after 10 seconds
setTimeout(() => controller.abort(), 10000);

try {
  const result = await resultPromise;
} catch (error) {
  if (error instanceof RetryAbortError) {
    console.log('Retry was cancelled');
  }
}
```

### With Maximum Delay Cap

```typescript
const result = await retry(fetchData, {
  retries: 10,
  delay: 1000,
  backoff: 'exponential',
  factor: 2,
  maxDelay: 10000, // Never wait more than 10s
});
// Delays: 1s, 2s, 4s, 8s, 10s, 10s, 10s...
```

## Error Handling

### RetryExhaustedError

Thrown when all retry attempts are exhausted.

```typescript
import { retry, RetryExhaustedError } from 'retry-fn';

try {
  await retry(unreliableFunction, { retries: 3 });
} catch (error) {
  if (error instanceof RetryExhaustedError) {
    console.log('All retries failed:', error.lastError);
    console.log('Total attempts:', error.attempts);
  }
}
```

### RetryAbortError

Thrown when retry is cancelled via AbortSignal.

```typescript
import { retry, RetryAbortError } from 'retry-fn';

try {
  await retry(asyncFn, { signal: controller.signal });
} catch (error) {
  if (error instanceof RetryAbortError) {
    console.log('Operation was cancelled');
  }
}
```

## TypeScript

Full TypeScript support with exported types:

```typescript
import {
  retry,
  RetryOptions,
  BackoffStrategy,
  BackoffFunction,
  OnRetryCallback,
  RetryCondition,
  RetryAbortError,
  RetryExhaustedError,
} from 'retry-fn';
```

## License

MIT