From bf139d9a676e62facf62f5af7a10e292ecb2056a Mon Sep 17 00:00:00 2001 From: Richard R Date: Mon, 6 Apr 2026 11:41:42 -0600 Subject: [PATCH] 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. --- src/lib/shared/tts-provider-catalog.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/shared/tts-provider-catalog.ts b/src/lib/shared/tts-provider-catalog.ts index fd26aff..c162ce0 100644 --- a/src/lib/shared/tts-provider-catalog.ts +++ b/src/lib/shared/tts-provider-catalog.ts @@ -181,8 +181,6 @@ async function fetchDeepinfraVoices(apiKey: string): Promise { }, }); - clearTimeout(timeoutId); - if (!response.ok) { throw new Error('Failed to fetch Deepinfra voices'); } @@ -195,12 +193,13 @@ async function fetchDeepinfraVoices(apiKey: string): Promise { } return []; } catch (error) { - clearTimeout(timeoutId); if (error instanceof DOMException && error.name === 'AbortError') { return []; } console.error('Error fetching Deepinfra voices:', error); return []; + } finally { + clearTimeout(timeoutId); } } @@ -220,18 +219,19 @@ async function fetchCustomOpenAiVoices(baseUrl: string, apiKey: string): Promise }, }); - clearTimeout(timeoutId); - if (!response.ok) { return null; } 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 { - clearTimeout(timeoutId); console.log('Custom endpoint does not support voices, using defaults'); return null; + } finally { + clearTimeout(timeoutId); } }