refactor(tts): ensure timeout cleanup and validate remote voice payloads

Make voice-fetching more robust by always clearing the request timeout and stricter type-checking of remote data.

- move clearTimeout into finally blocks for Deepinfra and custom OpenAI voice fetches to guarantee the timeout is cleared on success, failure, or abort
- validate that custom endpoint returns an array of strings before accepting voices; otherwise fall back to defaults

This reduces potential timer leaks and avoids accepting malformed voice lists from custom endpoints.
This commit is contained in:
Richard R 2026-04-06 11:41:42 -06:00
parent 09944ec4e4
commit bf139d9a67

View file

@ -181,8 +181,6 @@ async function fetchDeepinfraVoices(apiKey: string): Promise<string[]> {
}, },
}); });
clearTimeout(timeoutId);
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to fetch Deepinfra voices'); throw new Error('Failed to fetch Deepinfra voices');
} }
@ -195,12 +193,13 @@ async function fetchDeepinfraVoices(apiKey: string): Promise<string[]> {
} }
return []; return [];
} catch (error) { } catch (error) {
clearTimeout(timeoutId);
if (error instanceof DOMException && error.name === 'AbortError') { if (error instanceof DOMException && error.name === 'AbortError') {
return []; return [];
} }
console.error('Error fetching Deepinfra voices:', error); console.error('Error fetching Deepinfra voices:', error);
return []; return [];
} finally {
clearTimeout(timeoutId);
} }
} }
@ -220,18 +219,19 @@ async function fetchCustomOpenAiVoices(baseUrl: string, apiKey: string): Promise
}, },
}); });
clearTimeout(timeoutId);
if (!response.ok) { if (!response.ok) {
return null; return null;
} }
const data = await response.json(); const data = await response.json();
return Array.isArray(data.voices) ? data.voices : null; return Array.isArray(data.voices) && data.voices.every((voice: unknown) => typeof voice === 'string')
? data.voices
: null;
} catch { } catch {
clearTimeout(timeoutId);
console.log('Custom endpoint does not support voices, using defaults'); console.log('Custom endpoint does not support voices, using defaults');
return null; return null;
} finally {
clearTimeout(timeoutId);
} }
} }