From 9adee07b83a7ac782df43728b2046d4585e232c6 Mon Sep 17 00:00:00 2001 From: 0xJacobs Date: Mon, 9 Mar 2026 00:37:02 +0100 Subject: [PATCH] fix: add 30s network timeout to callSoloditAPI --- src/index.ts | 61 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/index.ts b/src/index.ts index ed853a8..090254f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -178,35 +178,48 @@ interface SoloditResponse { async function callSoloditAPI(body: SoloditRequest): Promise { await rateLimiter.waitIfNeeded(); - const res = await fetch(`${API_BASE}/findings`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Cyfrin-API-Key": API_KEY!, - }, - body: JSON.stringify(body), - }); - - rateLimiter.update(res.headers); - - if (!res.ok) { - const errBody = await res.text(); - if (res.status === 401) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout + + try { + const res = await fetch(`${API_BASE}/findings`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Cyfrin-API-Key": API_KEY!, + }, + body: JSON.stringify(body), + signal: controller.signal, + }); + + clearTimeout(timeoutId); + rateLimiter.update(res.headers); + + if (!res.ok) { + const errBody = await res.text(); + if (res.status === 401) { + throw new Error( + `Invalid API key. Get a new one at: https://solodit.cyfrin.io → Profile → API Keys` + ); + } + if (res.status === 429) { + throw new Error( + `Rate limited. Limit resets at ${new Date(rateLimiter.status().resetAt).toISOString()}. Try again shortly.` + ); + } throw new Error( - `Invalid API key. Get a new one at: https://solodit.cyfrin.io → Profile → API Keys` + `Solodit API error (${res.status}): ${errBody.slice(0, 200)}` ); } - if (res.status === 429) { - throw new Error( - `Rate limited. Limit resets at ${new Date(rateLimiter.status().resetAt).toISOString()}. Try again shortly.` - ); + + return (await res.json()) as SoloditResponse; + } catch (error: any) { + clearTimeout(timeoutId); + if (error.name === 'AbortError') { + throw new Error('Request timeout - Solodit API did not respond within 30 seconds'); } - throw new Error( - `Solodit API error (${res.status}): ${errBody.slice(0, 200)}` - ); + throw error; } - - return (await res.json()) as SoloditResponse; } // ── Formatters ──────────────────────────────────────────────────────────────