/** * Metered TURN Server Integration */ let globalIceServers = [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun.cloudflare.com:3478' }, ]; let meteredIceFetchPromise = null; /** * Fetch ICE servers from /ice-servers endpoint and merge with default STUN server. * Handles network errors gracefully with fallback. * * @returns {Promise} Resolves with the array of ICE server objects. */ export function getMeteredIceServers() { if (meteredIceFetchPromise) return meteredIceFetchPromise; meteredIceFetchPromise = fetch('/ice-servers', { headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }) .then(response => { if (!response.ok) { throw new Error(`Failed to fetch ICE servers: HTTP status ${response.status}`); } return response.json(); }) .then(meteredIce => { if (Array.isArray(meteredIce) && meteredIce.length > 0) { globalIceServers = [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun.cloudflare.com:3478' }, ...meteredIce ]; } if (typeof window !== 'undefined') { window.globalIceServers = globalIceServers; } return globalIceServers; }) .catch(e => { console.warn('Metered TURN fetch warning:', e); if (typeof window !== 'undefined') { window.globalIceServers = globalIceServers; } return globalIceServers; }); return meteredIceFetchPromise; } export function initMeteredIceServers() { return getMeteredIceServers(); } export { globalIceServers };