import type { RequestConfig, HttpResponse } from '../types'; import type { RequestAdapter } from './types'; import { ProxyManager } from '../proxy-manager'; import { HttpError } from '../types'; /** * Fetch adapter for HTTP/HTTPS proxies and non-proxy requests */ export class FetchAdapter implements RequestAdapter { canHandle(config: RequestConfig): boolean { // Fetch handles non-proxy requests and HTTP/HTTPS proxies return !config.proxy || config.proxy.protocol === 'http' || config.proxy.protocol === 'https'; } async request(config: RequestConfig, signal: AbortSignal): Promise> { const { url, method = 'GET', headers, data, proxy } = config; // Prepare fetch options const fetchOptions: RequestInit = { method, headers, signal, }; // Add body for non-GET requests if (data && method !== 'GET') { fetchOptions.body = typeof data === 'string' ? data : JSON.stringify(data); if (typeof data === 'object') { fetchOptions.headers = { 'Content-Type': 'application/json', ...fetchOptions.headers }; } } // Add proxy if needed (using Bun's built-in proxy support) if (proxy) { (fetchOptions as any).proxy = ProxyManager.createProxyUrl(proxy); } const response = await fetch(url, fetchOptions); // Parse response based on content type let responseData: T; const contentType = response.headers.get('content-type') || ''; if (contentType.includes('application/json')) { responseData = await response.json() as T; } else { responseData = await response.text() as T; } const httpResponse: HttpResponse = { data: responseData, status: response.status, headers: Object.fromEntries(response.headers.entries()), ok: response.ok, }; // Throw HttpError for non-2xx status codes if (!response.ok) { throw new HttpError( `Request failed with status ${response.status}`, response.status, httpResponse ); } return httpResponse; } }