@akomalabs/kemono
Version:
A production-grade TypeScript API wrapper for the Kemono/Coomer platforms
137 lines • 4.59 kB
JavaScript
import axios from 'axios';
import axiosRetry from 'axios-retry';
import { KemonoApiError, AuthenticationError, NotFoundError } from '../errors/api-error';
/**
* HTTP client for making API requests with retry, caching, and rate limiting
*/
export class HttpClient {
cache;
rateLimiter;
logger;
axios;
constructor(config, cache, rateLimiter, logger) {
this.cache = cache;
this.rateLimiter = rateLimiter;
this.logger = logger;
// Initialize axios instance
this.axios = axios.create({
baseURL: config.baseUrl || 'https://kemono.su/api/v1',
headers: {
'User-Agent': '@akomalabs/kemono',
...(config.sessionKey && { Cookie: `session=${config.sessionKey}` }),
},
});
// Configure retry behavior
axiosRetry(this.axios, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => {
return axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response?.status === 429;
},
});
// Add response interceptor for error handling
this.axios.interceptors.response.use((response) => response, (error) => {
if (!error.response) {
throw error;
}
switch (error.response.status) {
case 401:
throw new AuthenticationError();
case 404:
throw new NotFoundError();
default:
throw new KemonoApiError(`API Error: ${error.message}`, error.response.status, 'API_ERROR');
}
});
}
/**
* Make a GET request with caching and rate limiting
* @param url Request URL
* @param config Axios request config
* @returns Response data
*/
async get(url, config) {
const cacheKey = `get:${url}`;
// Check cache first
const cachedData = await this.cache.get(cacheKey);
if (cachedData) {
this.logger.debug('Cache hit', { url });
return JSON.parse(cachedData);
}
await this.rateLimiter.checkLimit();
try {
const response = await this.axios.get(url, config);
await this.rateLimiter.incrementCount();
// Cache the response
await this.cache.set(cacheKey, JSON.stringify(response.data));
return response.data;
}
catch (error) {
this.logger.error('GET request failed', { url, error });
throw error;
}
}
/**
* Make a POST request with rate limiting
* @param url Request URL
* @param data Request body
* @param config Axios request config
* @returns Response data
*/
async post(url, data, config) {
await this.rateLimiter.checkLimit();
try {
const response = await this.axios.post(url, data, config);
await this.rateLimiter.incrementCount();
return response.data;
}
catch (error) {
this.logger.error('POST request failed', { url, error });
throw error;
}
}
/**
* Make a PUT request with rate limiting
* @param url Request URL
* @param data Request body
* @param config Axios request config
* @returns Response data
*/
async put(url, data, config) {
await this.rateLimiter.checkLimit();
try {
const response = await this.axios.put(url, data, config);
await this.rateLimiter.incrementCount();
return response.data;
}
catch (error) {
this.logger.error('PUT request failed', { url, error });
throw error;
}
}
/**
* Make a DELETE request with rate limiting
* @param url Request URL
* @param config Axios request config
* @returns Response data
*/
async delete(url, config) {
await this.rateLimiter.checkLimit();
try {
const response = await this.axios.delete(url, config);
await this.rateLimiter.incrementCount();
return response.data;
}
catch (error) {
this.logger.error('DELETE request failed', { url, error });
throw error;
}
}
}
/**
* Create a new HTTP client instance
*/
export function createHttpClient(config, cache, rateLimiter, logger) {
return new HttpClient(config, cache, rateLimiter, logger);
}
//# sourceMappingURL=http-client.js.map