UNPKG

@toast-studios/asset-manager

Version:

A React Native asset management library with intelligent caching and loading strategies

324 lines (323 loc) 12.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NetworkMonitor = void 0; const netinfo_1 = __importDefault(require("@react-native-community/netinfo")); const index_1 = require("../types/index"); /** * Network monitoring utility for React Native - optimized for Indian low-end devices */ class NetworkMonitor { constructor(pingTimeoutMs = 5000, pingTargets = ['https://www.google.com/generate_204']) { this.isMonitoring = false; this.lastPingTime = 0; this.pingCacheDuration = 300000; // Cache ping for 5 minutes to reduce data usage this.pingTimeoutMs = pingTimeoutMs; this.pingTargets = pingTargets.length > 0 ? pingTargets : ['https://www.google.com/generate_204']; } /** * Get current network conditions */ async getCurrentConditions() { var _a, _b, _c; try { const netInfo = await netinfo_1.default.fetch(); const conditions = { type: this.mapNetworkType(netInfo.type), isConnected: (_a = netInfo.isConnected) !== null && _a !== void 0 ? _a : false, isMetered: (_c = (_b = netInfo.details) === null || _b === void 0 ? void 0 : _b.isConnectionExpensive) !== null && _c !== void 0 ? _c : false, }; // Add speed information if available if (netInfo.details && 'effectiveType' in netInfo.details) { conditions.speed = this.estimateSpeedFromType(netInfo.details.effectiveType); } // Only measure ping if connected and not on metered connection // Also cache ping to reduce data usage if (conditions.isConnected && !conditions.isMetered) { const now = Date.now(); if (!this.cachedPing || now - this.lastPingTime > this.pingCacheDuration) { try { conditions.ping = await this.measurePing(); this.cachedPing = conditions.ping; this.lastPingTime = now; } catch (error) { // Use cached ping if available, otherwise estimate conditions.ping = this.cachedPing || this.estimatePingFromConnectionType(conditions.type); console.warn('Failed to measure ping, using fallback:', error); } } else { conditions.ping = this.cachedPing; } } else { // For metered connections, estimate ping without network calls conditions.ping = this.estimatePingFromConnectionType(conditions.type); } return conditions; } catch (error) { console.error('Failed to get network conditions:', error); // Return safe defaults return { type: index_1.NetworkType.UNKNOWN, isConnected: false, }; } } /** * Start monitoring network changes */ startMonitoring(callback) { if (this.isMonitoring) { this.stopMonitoring(); } this.isMonitoring = true; this.unsubscribe = netinfo_1.default.addEventListener(async (netInfo) => { try { const conditions = await this.processNetInfo(netInfo); callback(conditions); } catch (error) { console.error('Error processing network info:', error); } }); } /** * Stop monitoring network changes */ stopMonitoring() { if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = undefined; } this.isMonitoring = false; } /** * Static method to test ping target configuration (for testing/debugging purposes) * * @param pingTargets Array of ping target URLs to test * @param pingTimeout Timeout in milliseconds * @returns Promise with ping results */ static async testPingTargets(pingTargets = ['https://www.google.com/generate_204'], pingTimeout = 5000) { const monitor = new NetworkMonitor(pingTimeout, pingTargets); const results = []; for (const target of pingTargets) { try { const ping = await monitor.pingUrl(target); results.push({ url: target, ping }); } catch (error) { results.push({ url: target, error: error instanceof Error ? error.message : String(error), }); } } // Calculate median of successful pings const successfulPings = results .filter(result => result.ping !== undefined) .map(result => result.ping); let median; if (successfulPings.length > 0) { successfulPings.sort((a, b) => a - b); const midIndex = Math.floor(successfulPings.length / 2); median = successfulPings.length % 2 === 0 ? (successfulPings[midIndex - 1] + successfulPings[midIndex]) / 2 : successfulPings[midIndex]; } return { targets: pingTargets, results, median, }; } /** * Measure network ping to configured endpoints - optimized for data efficiency */ async measurePing() { // Use configured ping targets (defaults to single Google endpoint for data efficiency) const targets = this.pingTargets; if (targets.length === 1) { // Single target - direct measurement for data efficiency try { const ping = await this.pingUrl(targets[0]); return ping; } catch (error) { throw new Error(`Ping measurement failed: ${error}`); } } else { // Multiple targets - measure all and return median const measurements = await Promise.allSettled(targets.map(url => this.pingUrl(url))); // Get successful measurements const successfulPings = measurements .filter((result) => result.status === 'fulfilled' && result.value > 0) .map(result => result.value); if (successfulPings.length === 0) { throw new Error('All ping measurements failed'); } // Return median ping to reduce impact of outliers successfulPings.sort((a, b) => a - b); const midIndex = Math.floor(successfulPings.length / 2); return successfulPings.length % 2 === 0 ? (successfulPings[midIndex - 1] + successfulPings[midIndex]) / 2 : successfulPings[midIndex]; } } /** * Ping a specific URL and measure response time */ async pingUrl(url) { return new Promise((resolve, reject) => { const startTime = Date.now(); const timeout = setTimeout(() => { reject(new Error(`Ping timeout for ${url}`)); }, this.pingTimeoutMs); fetch(url, { method: 'HEAD', mode: 'no-cors', // Avoid CORS issues }) .then(() => { clearTimeout(timeout); const ping = Date.now() - startTime; resolve(ping); }) .catch(error => { clearTimeout(timeout); reject(error); }); }); } /** * Process NetInfo object into NetworkConditions */ async processNetInfo(netInfo) { var _a, _b, _c; const conditions = { type: this.mapNetworkType(netInfo.type), isConnected: (_a = netInfo.isConnected) !== null && _a !== void 0 ? _a : false, isMetered: (_c = (_b = netInfo.details) === null || _b === void 0 ? void 0 : _b.isConnectionExpensive) !== null && _c !== void 0 ? _c : false, }; // Add speed estimation if (netInfo.details && 'effectiveType' in netInfo.details) { conditions.speed = this.estimateSpeedFromType(netInfo.details.effectiveType); } else { conditions.speed = this.estimateSpeedFromConnectionType(conditions.type); } // Only measure ping on non-metered connections to save data if (conditions.isConnected && !conditions.isMetered) { const now = Date.now(); if (!this.cachedPing || now - this.lastPingTime > this.pingCacheDuration) { // Non-blocking ping measurement this.measurePing() .then(ping => { conditions.ping = ping; this.cachedPing = ping; this.lastPingTime = now; }) .catch(() => { // Use cached or estimated ping conditions.ping = this.cachedPing || this.estimatePingFromConnectionType(conditions.type); }); } else { conditions.ping = this.cachedPing; } } else { // Estimate ping for metered connections conditions.ping = this.estimatePingFromConnectionType(conditions.type); } return conditions; } /** * Map NetInfo network type to our NetworkType enum */ mapNetworkType(netInfoType) { switch (netInfoType.toLowerCase()) { case 'wifi': return index_1.NetworkType.WIFI; case 'cellular': case 'mobile': return index_1.NetworkType.CELLULAR; case 'ethernet': return index_1.NetworkType.ETHERNET; default: return index_1.NetworkType.UNKNOWN; } } /** * Estimate speed from effective connection type - optimized for Indian networks */ estimateSpeedFromType(effectiveType) { // These are realistic estimates for Indian network conditions switch (effectiveType.toLowerCase()) { case 'slow-2g': return 0.05; // 50 Kbps case '2g': return 0.25; // 250 Kbps case '3g': return 1.5; // 1.5 Mbps case '4g': return 8; // 8 Mbps (more realistic for India) case '5g': return 50; // 50 Mbps (still not widely available) default: return 2; // Conservative default } } /** * Estimate speed from connection type when effective type is unavailable */ estimateSpeedFromConnectionType(type) { switch (type) { case index_1.NetworkType.ETHERNET: return 50; // Usually fast but may vary in India case index_1.NetworkType.WIFI: return 15; // More realistic for Indian WiFi case index_1.NetworkType.CELLULAR: return 3; // Conservative estimate for cellular default: return 1; // Very conservative estimate } } /** * Estimate ping from connection type without network calls */ estimatePingFromConnectionType(type) { switch (type) { case index_1.NetworkType.ETHERNET: return 50; // Generally good latency case index_1.NetworkType.WIFI: return 100; // Typical WiFi latency in India case index_1.NetworkType.CELLULAR: return 200; // Higher latency for cellular default: return 300; // Conservative estimate } } /** * Cleanup resources */ destroy() { this.stopMonitoring(); } } exports.NetworkMonitor = NetworkMonitor;