| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| interface TimeoutOptions {
|
| timeoutMs?: number;
|
| label?: string;
|
| signal?: AbortSignal;
|
| }
|
|
|
| export async function fetchWithTimeout(url: string, options: RequestInit & TimeoutOptions = {}) {
|
| const { timeoutMs = 30000, label = "Request", signal: externalSignal, ...fetchOptions } = options;
|
|
|
| const controller = new AbortController();
|
|
|
|
|
| if (externalSignal) {
|
| externalSignal.addEventListener("abort", () => controller.abort(externalSignal.reason));
|
| }
|
|
|
| const timeoutId = setTimeout(() => {
|
| controller.abort(new Error(`${label} timed out after ${timeoutMs}ms`));
|
| }, timeoutMs);
|
|
|
| try {
|
| const response = await fetch(url, {
|
| ...fetchOptions,
|
| signal: controller.signal,
|
| });
|
| return response;
|
| } catch (error: any) {
|
| if (error.name === "AbortError" || controller.signal.aborted) {
|
| const timeoutError: any = new Error(`${label} timed out after ${timeoutMs}ms`);
|
| timeoutError.name = "TimeoutError";
|
| timeoutError.originalUrl = url;
|
| timeoutError.timeoutMs = timeoutMs;
|
| throw timeoutError;
|
| }
|
| throw error;
|
| } finally {
|
| clearTimeout(timeoutId);
|
| }
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export async function withTimeout<T>(
|
| fn: () => Promise<T>,
|
| timeoutMs: number,
|
| label = "Operation"
|
| ): Promise<T> {
|
| return new Promise<T>((resolve, reject) => {
|
| const timeoutId = setTimeout(() => {
|
| const error: any = new Error(`${label} timed out after ${timeoutMs}ms`);
|
| error.name = "TimeoutError";
|
| error.timeoutMs = timeoutMs;
|
| reject(error);
|
| }, timeoutMs);
|
|
|
| fn()
|
| .then((result) => {
|
| clearTimeout(timeoutId);
|
| resolve(result);
|
| })
|
| .catch((error) => {
|
| clearTimeout(timeoutId);
|
| reject(error);
|
| });
|
| });
|
| }
|
|
|
| |
| |
|
|
| export const PROVIDER_TIMEOUTS: Record<string, number> = {
|
| openai: 60000,
|
| claude: 90000,
|
| gemini: 60000,
|
| codex: 120000,
|
| qwen: 45000,
|
| deepseek: 60000,
|
| cohere: 45000,
|
| groq: 30000,
|
| mistral: 45000,
|
| openrouter: 60000,
|
| default: 60000,
|
| };
|
|
|
| export function getProviderTimeout(provider: string): number {
|
| return PROVIDER_TIMEOUTS[provider] || PROVIDER_TIMEOUTS.default;
|
| }
|
|
|