fix(tts): avoid char-by-char split when signature list is empty

SignatureClient.regex returned new RegExp("()", "g") when nameSet
was empty (server unreachable, list not yet synced). text.split() with
that regex matches every zero-width position, so 'abc' became
['a','','b','','c',''] and TTSClient sent a separate Chzzk request
per character.

Two-layer fix:
- SignatureClient.regex returns /$^/g (never-match) when names is
  empty, so split() returns the original string as a single element.
- TTSClient.getSource explicitly skips split when nameSet.size === 0
  so the intent is obvious at the call site.
This commit is contained in:
Claude Owner
2026-05-26 14:48:24 +09:00
parent 08de63b448
commit 499852b2a7
2 changed files with 11 additions and 2 deletions

View File

@@ -35,7 +35,12 @@ export class SignatureClient {
}
get regex() {
const escaped = [...this.nameSet].map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
const names = [...this.nameSet];
// 빈 목록이면 new RegExp("()", "g")가 모든 위치에서 매칭돼
// text.split() 결과가 글자 단위로 쪼개진다. 절대 매칭되지 않는
// 패턴을 돌려줘 split 호출자가 원문을 그대로 받게 한다.
if (names.length === 0) return /$^/g;
const escaped = names.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
return new RegExp(`(${escaped.join("|")})`, "g");
}

View File

@@ -92,7 +92,11 @@ export class TTSClient {
private async getSource(text: string): Promise<Buffer | null> {
const parts = text.split(signature.regex);
// 시그니처 목록이 비어 있으면 split을 건너뛰고 원문을 한 번에 합성한다.
// (SignatureClient.regex가 빈 목록일 때 never-match 패턴을 돌려주지만,
// 소비자 측에서도 명시적으로 가드해 의도를 분명히 한다.)
const names = signature.nameSet;
const parts = names.size === 0 ? [text] : text.split(signature.regex);
const bufferList: Buffer[] = [];
for (const part of parts) {