npm package: @rynn-k/proxy-agent
Description: Efficient proxy rotation agent for Node.js with seamless axios integration
Author: rynn
Latest version: 1.1.0

# @rynn-k/proxy-agent

Efficient proxy rotation agent for Node.js with seamless axios integration. Supports HTTP, HTTPS, SOCKS4, and SOCKS5 proxies with both authenticated and non-authenticated modes, automatic rotation, health checking, and comprehensive error handling.

## 🚀 Features

- ✅ **Smart Proxy Rotation** - Sequential or random proxy selection
- ✅ **Multiple Protocol Support** - HTTP, HTTPS, SOCKS4, and SOCKS5
- ✅ **Multiple Auth Types** - Support for authenticated and non-authenticated proxies  
- ✅ **Axios Integration** - Drop-in replacement for axios HTTP/HTTPS agents
- ✅ **Auto Validation** - Built-in IP and port validation
- ✅ **Health Checking** - Test proxy connectivity and response times
- ✅ **Auto Reload** - Dynamic proxy file reloading
- ✅ **Multiple Formats** - Flexible proxy file format support (URL and colon-separated)
- ✅ **TypeScript Ready** - Full TypeScript definitions included
- ✅ **Minimal Dependencies** - Only requires proxy agent libraries

## 📦 Installation

```bash
npm install @rynn-k/proxy-agent
```

All required dependencies (`https-proxy-agent`, `http-proxy-agent`, `socks-proxy-agent`) will be installed automatically.

## 🔧 Quick Start

### 1. Create Proxy File

Create `proxies.txt` with your proxy list:

```txt
# HTTP/HTTPS proxies (ip:port)
192.168.1.1:8080
10.0.0.1:3128

# SOCKS5 proxies (URL format)
socks5://192.168.1.10:1080
socks5://username:password@proxy.example.com:1080

# SOCKS4 proxies
socks4://192.168.1.11:1080

# Authenticated HTTP proxies (ip:port:username:password)  
192.168.1.2:8080:user123:pass456
proxy.example.com:3128:myuser:mypass

# Protocol-specific colon format (protocol:ip:port)
http:192.168.1.3:8080
https:secure-proxy.com:8080
socks5:192.168.1.4:1080

# Authenticated with protocol (protocol:ip:port:username:password)
http:192.168.1.5:8080:user:pass
socks5:192.168.1.6:1080:user:pass
```

### 2. Basic Usage

```javascript
const ProxyAgent = require('@rynn-k/proxy-agent');
const axios = require('axios');

// Initialize proxy agent
const proxy = new ProxyAgent('./proxies.txt');

// Use with axios
const response = await axios.get('https://httpbin.org/ip', proxy.config());
console.log('Your IP:', response.data.origin);
```

### 3. Advanced Configuration

```javascript
const proxy = new ProxyAgent('./proxies.txt', {
  random: true,        // Random proxy selection (default: false)
  log: true,           // Enable logging (default: true)  
  autoReload: true,    // Auto-reload on file change (default: false)
  encoding: 'utf-8',   // File encoding (default: 'utf-8')
  type: 'socks5'       // Default proxy type when not specified (default: 'http')
});
```

## 📖 API Documentation

### Constructor

```javascript
new ProxyAgent(proxyFilePath, options)
```

**Parameters:**
- `proxyFilePath` (string): Path to proxy file (default: 'proxies.txt')
- `options` (object): Configuration options

**Options:**
- `random` (boolean): Use random proxy selection instead of sequential
- `log` (boolean): Enable/disable logging  
- `autoReload` (boolean): Auto-reload proxy file on changes
- `encoding` (string): File encoding for reading proxy file
- `type` (string): Default proxy type ('http', 'https', 'socks4', 'socks5') when not specified in proxy string

### Methods

#### `config()`
Returns axios configuration object with proxy agents configured.

```javascript
const response = await axios.get('https://example.com', proxy.config());
```

#### `https()`
Returns HTTPS proxy agent (or SOCKS agent for SOCKS proxies).

```javascript
const httpsAgent = proxy.https();
const response = await axios.get('https://example.com', { httpsAgent, proxy: false });
```

#### `http()`
Returns HTTP proxy agent (or SOCKS agent for SOCKS proxies).

```javascript
const httpAgent = proxy.http();
```

#### `getCurrentProxy()`
Get information about the currently selected proxy.

```javascript
const current = proxy.getCurrentProxy();
console.log(current);
// { ip: '192.168.1.1', port: 8080, hasAuth: false, protocol: 'http', index: 0 }
```

#### `test(testUrl, timeout, useCurrentProxy)`
Test proxy connectivity and measure response time.

```javascript
const result = await proxy.test('https://httpbin.org/ip', 10000);
console.log(result);
// { success: true, proxy: 'socks5://192.168.1.10:1080', time: 245, data: {...} }
```

**Parameters:**
- `testUrl` (string): URL to test (default: 'https://httpbin.org/ip')
- `timeout` (number): Request timeout in milliseconds (default: 10000)
- `useCurrentProxy` (boolean): Use current proxy or get next (default: true)

#### `list()`
Get information about all loaded proxies.

```javascript
const proxies = proxy.list();
console.log(proxies);
// [
//   { index: 0, ip: '192.168.1.1', port: 8080, hasAuth: false, protocol: 'http', current: true },
//   { index: 1, ip: '192.168.1.10', port: 1080, hasAuth: true, protocol: 'socks5', current: false }
// ]
```

#### `stats()`
Get proxy agent statistics including protocol distribution.

```javascript
const stats = proxy.stats();
console.log(stats);
// {
//   total: 10,
//   auth: 5,
//   noAuth: 5,
//   protocols: { http: 4, https: 2, socks5: 3, socks4: 1 },
//   currentIndex: 2,
//   lastUsedIndex: 1,
//   random: false,
//   file: '/path/to/proxies.txt',
//   autoReload: false,
//   currentProxy: { ip: '192.168.1.1', port: 8080, hasAuth: false, protocol: 'http', index: 1 }
// }
```

#### `reload()`
Manually reload proxies from file.

```javascript
proxy.reload();
```

#### `destroy()`
Clean up resources and stop file watching.

```javascript
proxy.destroy();
```

## 💡 Usage Examples

### Basic Rotation with SOCKS5

```javascript
const ProxyAgent = require('@rynn-k/proxy-agent');
const axios = require('axios');

// Create proxies.txt with SOCKS5 proxies
// socks5://192.168.1.10:1080
// socks5://user:pass@192.168.1.11:1080

const proxy = new ProxyAgent('./proxies.txt');

// Each request uses the next proxy in rotation
for (let i = 0; i < 5; i++) {
  try {
    const response = await axios.get('https://httpbin.org/ip', proxy.config());
    console.log(`Request ${i + 1}:`, response.data.origin);
  } catch (error) {
    console.error(`Request ${i + 1} failed:`, error.message);
  }
}
```

### Mixed Protocol Proxies

```javascript
// proxies.txt contains mixed protocols:
// http://192.168.1.1:8080
// socks5://192.168.1.10:1080
// socks4://192.168.1.11:1080
// https://secure.proxy.com:8080

const proxy = new ProxyAgent('./proxies.txt', {
  random: true,  // Randomly select from all protocols
  log: true
});

// The agent automatically uses the correct protocol for each proxy
const response = await axios.get('https://api.example.com/data', proxy.config());
```

### Default Protocol Configuration

```javascript
// If your proxies.txt doesn't specify protocols:
// 192.168.1.10:1080
// 192.168.1.11:1080
// 192.168.1.12:1080:user:pass

// Set default protocol to SOCKS5
const proxy = new ProxyAgent('./proxies.txt', {
  type: 'socks5',  // All proxies without protocol will be treated as SOCKS5
  log: true
});

const response = await axios.get('https://example.com', proxy.config());
```

### Error Handling & Retry with Protocol Fallback

```javascript
async function robustRequest(url, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await axios.get(url, {
        ...proxy.config(),
        timeout: 10000
      });
      
      const currentProxy = proxy.getCurrentProxy();
      console.log(`✅ Success with ${currentProxy.protocol}://${currentProxy.ip}:${currentProxy.port}`);
      
      return response.data;
    } catch (error) {
      const currentProxy = proxy.getCurrentProxy();
      console.log(`❌ Attempt ${attempt} failed with ${currentProxy.protocol}://${currentProxy.ip}:${currentProxy.port}`);
      
      if (attempt === maxRetries) {
        throw new Error(`All ${maxRetries} attempts failed`);
      }
      
      // Wait before retry with different proxy
      await new Promise(resolve => setTimeout(resolve, 2000));
    }
  }
}

// Usage
try {
  const data = await robustRequest('https://api.example.com/data');
  console.log(data);
} catch (error) {
  console.error('Request failed after all retries:', error.message);
}
```

### Axios Instance Integration

```javascript
const axiosWithProxy = axios.create({
  timeout: 15000,
  headers: {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
  }
});

// Add proxy rotation to every request
axiosWithProxy.interceptors.request.use(config => {
  const proxyConfig = proxy.config();
  config.httpsAgent = proxyConfig.httpsAgent;
  config.httpAgent = proxyConfig.httpAgent;
  config.proxy = false;
  return config;
});

// Now all requests automatically use rotating proxies
const response = await axiosWithProxy.get('https://api.example.com/data');
```

### Health Checking All Protocols

```javascript
// Test all proxies and filter working ones
const proxy = new ProxyAgent('./proxies.txt', { log: false });
const workingProxies = [];
const failedProxies = [];

const allProxies = proxy.list();

for (let i = 0; i < allProxies.length; i++) {
  const proxyInfo = allProxies[i];
  
  try {
    const result = await proxy.test('https://httpbin.org/ip', 10000, false);
    
    if (result.success) {
      workingProxies.push({
        ...proxyInfo,
        responseTime: result.time
      });
      console.log(`✅ ${proxyInfo.protocol}://${proxyInfo.ip}:${proxyInfo.port} - ${result.time}ms`);
    } else {
      failedProxies.push(proxyInfo);
      console.log(`❌ ${proxyInfo.protocol}://${proxyInfo.ip}:${proxyInfo.port} - ${result.error}`);
    }
  } catch (error) {
    failedProxies.push(proxyInfo);
    console.log(`❌ ${proxyInfo.protocol}://${proxyInfo.ip}:${proxyInfo.port} - ${error.message}`);
  }
}

console.log(`\n📊 Results:`);
console.log(`   Working: ${workingProxies.length}`);
console.log(`   Failed: ${failedProxies.length}`);

// Sort by response time
workingProxies.sort((a, b) => a.responseTime - b.responseTime);
console.log('\n⚡ Fastest proxies:');
workingProxies.slice(0, 3).forEach(p => {
  console.log(`   ${p.protocol}://${p.ip}:${p.port} - ${p.responseTime}ms`);
});
```

### Protocol-Specific Usage

```javascript
// Use only SOCKS5 proxies for specific tasks
const socksProxy = new ProxyAgent('./socks5-proxies.txt', {
  type: 'socks5'
});

// Use only HTTP proxies for other tasks
const httpProxy = new ProxyAgent('./http-proxies.txt', {
  type: 'http'
});

// Different requests with different proxy types
const socksResponse = await axios.get('https://api1.example.com', socksProxy.config());
const httpResponse = await axios.get('https://api2.example.com', httpProxy.config());
```

## 🔧 Proxy File Formats

The library supports multiple proxy formats:

### URL Format (Recommended)

```txt
# HTTP/HTTPS
http://192.168.1.1:8080
https://secure-proxy.com:8080
http://username:password@192.168.1.2:8080

# SOCKS5
socks5://192.168.1.10:1080
socks5://username:password@192.168.1.11:1080

# SOCKS4
socks4://192.168.1.12:1080
```

### Colon-Separated Format

```txt
# Basic format (ip:port) - uses default type from options
192.168.1.1:8080
proxy.example.com:3128

# With protocol (protocol:ip:port)
http:192.168.1.1:8080
socks5:192.168.1.10:1080
socks4:192.168.1.11:1080

# With authentication (ip:port:username:password)
192.168.1.2:8080:user123:password123
proxy.example.com:3128:myuser:mypass

# With protocol and authentication (protocol:ip:port:username:password)
http:192.168.1.3:8080:user:pass
socks5:192.168.1.10:1080:user:pass
```

### Comments and Empty Lines

```txt
# This is a comment
192.168.1.1:8080

# Empty lines are ignored

socks5://192.168.1.10:1080  # Inline comments are not supported
```

## 🔍 Protocol Support Details

| Protocol | Authentication | URL Format | Colon Format |
|----------|---------------|------------|--------------|
| HTTP     | ✅ Yes        | ✅ Yes     | ✅ Yes       |
| HTTPS    | ✅ Yes        | ✅ Yes     | ✅ Yes       |
| SOCKS5   | ✅ Yes        | ✅ Yes     | ✅ Yes       |
| SOCKS4   | ❌ No         | ✅ Yes     | ✅ Yes       |

**Note:** SOCKS4 protocol does not support authentication. If you specify credentials with SOCKS4, they will be ignored.

## 📊 Statistics and Monitoring

```javascript
const proxy = new ProxyAgent('./proxies.txt');

// Get detailed statistics
const stats = proxy.stats();
console.log('Total proxies:', stats.total);
console.log('Authenticated:', stats.auth);
console.log('Protocol distribution:', stats.protocols);
// Output: { http: 5, https: 3, socks5: 8, socks4: 2 }

// Monitor current proxy
setInterval(() => {
  const current = proxy.getCurrentProxy();
  if (current) {
    console.log(`Currently using: ${current.protocol}://${current.ip}:${current.port}`);
  }
}, 5000);
```

## ⚙️ TypeScript Support

Full TypeScript definitions are included:

```typescript
import ProxyAgent from '@rynn-k/proxy-agent';

const proxy = new ProxyAgent('./proxies.txt', {
  random: true,
  type: 'socks5'
});

// Type-safe proxy info
const info: ProxyAgent.CurrentProxyInfo | null = proxy.getCurrentProxy();

// Type-safe stats
const stats: ProxyAgent.ProxyStats = proxy.stats();
```

## 🐛 Troubleshooting

### SOCKS proxies not working

Make sure the module is properly installed with all dependencies:
```bash
npm install @rynn-k/proxy-agent
```

### Authentication issues with SOCKS4

SOCKS4 does not support authentication. Use SOCKS5 instead:
```txt
# Change from
socks4://user:pass@192.168.1.1:1080

# To
socks5://user:pass@192.168.1.1:1080
```

### Mixed protocol errors

Ensure each proxy line specifies the correct protocol or set a default type:
```javascript
const proxy = new ProxyAgent('./proxies.txt', {
  type: 'http'  // Default for lines without protocol
});
```

## 📝 License

MIT