/** Internal pull-wait ownership used by the node-host long poll. */ export class MeetingNodeAudioPullWaiters { readonly #waiters = new Set<() => void>(); get size(): number { return this.#waiters.size; } async wait(timeoutMs: number): Promise { let wake!: () => void; const ready = new Promise((resolve) => { wake = resolve; this.#waiters.add(wake); }); let timer: ReturnType | undefined; const timeout = new Promise((resolve) => { timer = setTimeout(resolve, timeoutMs); }); try { await Promise.race([timeout, ready]); } finally { // A stalled bridge can be polled indefinitely. Timeout must release its // resolver instead of retaining one waiter per empty pull. this.#waiters.delete(wake); if (timer !== undefined) { clearTimeout(timer); } } } wake(): void { const waiters = [...this.#waiters]; this.#waiters.clear(); for (const waiter of waiters) { waiter(); } } }