@toast-studios/asset-manager
Version:
A React Native asset management library with intelligent caching and loading strategies
324 lines (323 loc) • 12.4 kB
JavaScript
"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;