UNPKG

peerpigeon

Version:

WebRTC-based peer-to-peer mesh networking library with intelligent routing and signaling server

517 lines (450 loc) 15.5 kB
/** * EnvironmentDetector - Utility for detecting runtime environment and capabilities * Provides comprehensive environment detection for browser, Node.js, Worker contexts */ import DebugLogger from './DebugLogger.js'; export class EnvironmentDetector { constructor() { this._cache = new Map(); this._init(); } _init() { // Pre-compute common detections to avoid repeated checks this._cache.set('isBrowser', this._detectBrowser()); this._cache.set('isNodeJS', this._detectNodeJS()); this._cache.set('isWorker', this._detectWorker()); this._cache.set('isServiceWorker', this._detectServiceWorker()); this._cache.set('isWebWorker', this._detectWebWorker()); this._cache.set('isSharedWorker', this._detectSharedWorker()); this._cache.set('isDeno', this._detectDeno()); this._cache.set('isBun', this._detectBun()); this._cache.set('isNativeScript', this._detectNativeScript()); } // Primary environment detection _detectBrowser() { return typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined'; } _detectNodeJS() { return typeof process !== 'undefined' && process.versions != null && process.versions.node != null; } _detectDeno() { return typeof Deno !== 'undefined'; } _detectBun() { return typeof Bun !== 'undefined'; } _detectNativeScript() { return typeof global !== 'undefined' && ((typeof global.NativeScriptGlobals !== 'undefined') || (typeof global.__ANDROID__ !== 'undefined') || (typeof global.__IOS__ !== 'undefined') || (typeof global.__VISIONOS__ !== 'undefined') || (typeof global.loadModule === 'function' && typeof global.registerModule === 'function')); } _detectWorker() { return typeof importScripts !== 'undefined' || this._detectServiceWorker() || this._detectWebWorker() || this._detectSharedWorker(); } _detectServiceWorker() { return typeof globalThis.ServiceWorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof globalThis.ServiceWorkerGlobalScope; } _detectWebWorker() { return typeof globalThis.DedicatedWorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof globalThis.DedicatedWorkerGlobalScope; } _detectSharedWorker() { return typeof globalThis.SharedWorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof globalThis.SharedWorkerGlobalScope; } // Public getters get isBrowser() { return this._cache.get('isBrowser'); } get isNodeJS() { return this._cache.get('isNodeJS'); } get isWorker() { return this._cache.get('isWorker'); } get isServiceWorker() { return this._cache.get('isServiceWorker'); } get isWebWorker() { return this._cache.get('isWebWorker'); } get isSharedWorker() { return this._cache.get('isSharedWorker'); } get isDeno() { return this._cache.get('isDeno'); } get isBun() { return this._cache.get('isBun'); } get isNativeScript() { return this._cache.get('isNativeScript'); } get isServer() { return this.isNodeJS || this.isDeno || this.isBun; } get isClient() { return this.isBrowser || this.isWorker || this.isNativeScript; } // WebRTC capability detection get hasWebRTC() { if (this.isBrowser) { return typeof RTCPeerConnection !== 'undefined' || typeof webkitRTCPeerConnection !== 'undefined' || typeof mozRTCPeerConnection !== 'undefined'; } if (this.isNodeJS) { // Check for Node.js WebRTC implementations or globally injected WebRTC return (typeof global !== 'undefined' && typeof global.RTCPeerConnection !== 'undefined') || typeof RTCPeerConnection !== 'undefined'; } if (this.isNativeScript) { // NativeScript can access native WebRTC implementations through native modules // Check for globally injected WebRTC first return (typeof global !== 'undefined' && typeof global.RTCPeerConnection !== 'undefined') || typeof RTCPeerConnection !== 'undefined'; } return false; } get hasDataChannel() { return this.hasWebRTC && (this.isBrowser || this.isNativeScript); } get hasGetUserMedia() { if (!this.isBrowser && !this.isNativeScript) return false; return !!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) || !!(navigator && (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia)); } // WebSocket capability detection get hasWebSocket() { if (this.isBrowser || this.isWorker) { return typeof WebSocket !== 'undefined'; } if (this.isNodeJS) { // Check for globally injected WebSocket first if (typeof global !== 'undefined' && typeof global.WebSocket !== 'undefined') { return true; } if (typeof WebSocket !== 'undefined') { return true; } try { // Try ES module import first if (typeof require !== 'undefined') { require.resolve('ws'); return true; } else { // In ES modules, we can't easily check if a package is available // without actually importing it, so we'll assume it might be available return false; } } catch (e) { return false; } } if (this.isNativeScript) { // NativeScript has WebSocket support through global injection or native modules return typeof WebSocket !== 'undefined' || (typeof global !== 'undefined' && typeof global.WebSocket !== 'undefined'); } return false; } // Storage capability detection get hasLocalStorage() { if (!this.isBrowser && !this.isNativeScript) return false; try { const test = '__storage_test__'; localStorage.setItem(test, test); localStorage.removeItem(test); return true; } catch (e) { return false; } } get hasSessionStorage() { if (!this.isBrowser && !this.isNativeScript) return false; try { const test = '__storage_test__'; sessionStorage.setItem(test, test); sessionStorage.removeItem(test); return true; } catch (e) { return false; } } get hasIndexedDB() { if (!this.isBrowser) return false; // NativeScript doesn't typically have IndexedDB return typeof indexedDB !== 'undefined'; } get hasCookies() { if (!this.isBrowser) return false; // NativeScript doesn't have cookies return typeof document !== 'undefined' && typeof document.cookie === 'string'; } // Network and connectivity detection get hasNetworkInformation() { if (!this.isBrowser) return false; return typeof navigator !== 'undefined' && 'connection' in navigator; } get isOnline() { if (this.isBrowser) { return typeof navigator !== 'undefined' ? navigator.onLine : true; } return true; // Assume online for server environments } get networkType() { if (this.hasNetworkInformation) { return navigator.connection.effectiveType || 'unknown'; } return 'unknown'; } // Crypto capabilities get hasCrypto() { if (this.isBrowser || this.isWorker) { return typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined'; } if (this.isNodeJS) { try { if (typeof require !== 'undefined') { require('crypto'); return true; } else { // In ES modules, crypto is a built-in Node.js module return typeof process !== 'undefined' && process.versions.node; } } catch (e) { return false; } } if (this.isNativeScript) { // NativeScript may have crypto through global polyfills or native modules return (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') || (typeof global !== 'undefined' && typeof global.crypto !== 'undefined'); } return false; } get hasRandomValues() { if (this.isBrowser || this.isWorker) { return typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function'; } if (this.isNodeJS) { try { if (typeof require !== 'undefined') { const crypto = require('crypto'); return typeof crypto.randomBytes === 'function'; } else { // In ES modules, crypto is a built-in Node.js module return typeof process !== 'undefined' && process.versions.node; } } catch (e) { return false; } } if (this.isNativeScript) { // NativeScript may have crypto random values through global polyfills return (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') || (typeof global !== 'undefined' && typeof global.crypto !== 'undefined' && typeof global.crypto.getRandomValues === 'function'); } return false; } // Performance and timing get hasPerformanceNow() { return typeof performance !== 'undefined' && typeof performance.now === 'function'; } get hasHighResolutionTime() { if (this.isNodeJS) { return typeof process.hrtime === 'function' || typeof process.hrtime.bigint === 'function'; } return this.hasPerformanceNow; } // Browser-specific detection getBrowserInfo() { if (!this.isBrowser) return null; const userAgent = navigator.userAgent; const browsers = { chrome: /Chrome\/(\d+)/.exec(userAgent), firefox: /Firefox\/(\d+)/.exec(userAgent), safari: /Safari\/(\d+)/.exec(userAgent) && !/Chrome/.test(userAgent), edge: /Edge\/(\d+)/.exec(userAgent), ie: /MSIE (\d+)/.exec(userAgent) || /Trident.*rv:(\d+)/.exec(userAgent) }; for (const [browser, match] of Object.entries(browsers)) { if (match) { return { name: browser, version: match[1] || 'unknown' }; } } return { name: 'unknown', version: 'unknown' }; } // Node.js-specific detection getNodeInfo() { if (!this.isNodeJS) return null; return { version: process.version, platform: process.platform, arch: process.arch, versions: process.versions }; } // NativeScript-specific detection getNativeScriptInfo() { if (!this.isNativeScript) return null; const info = { runtime: 'nativescript' }; // Detect platform if globals are available if (typeof global !== 'undefined') { if (global.__ANDROID__) { info.platform = 'android'; } else if (global.__IOS__) { info.platform = 'ios'; } else if (global.__VISIONOS__) { info.platform = 'visionos'; } // Add additional NativeScript globals if available if (global.NativeScriptGlobals) { info.launched = global.NativeScriptGlobals.launched; info.appInstanceReady = global.NativeScriptGlobals.appInstanceReady; } } return info; } // Device and platform detection getPlatformInfo() { if (this.isBrowser) { return { userAgent: navigator.userAgent, platform: navigator.platform, language: navigator.language, languages: navigator.languages || [navigator.language], cookieEnabled: navigator.cookieEnabled, onLine: navigator.onLine, hardwareConcurrency: navigator.hardwareConcurrency || 1 }; } if (this.isNodeJS) { return this.getNodeInfo(); } if (this.isNativeScript) { return this.getNativeScriptInfo(); } return null; } // Feature detection for specific APIs hasFeature(feature) { const features = { // WebRTC webrtc: () => this.hasWebRTC, datachannel: () => this.hasDataChannel, getusermedia: () => this.hasGetUserMedia, // WebSocket websocket: () => this.hasWebSocket, // Storage localstorage: () => this.hasLocalStorage, sessionstorage: () => this.hasSessionStorage, indexeddb: () => this.hasIndexedDB, cookies: () => this.hasCookies, // Crypto crypto: () => this.hasCrypto, randomvalues: () => this.hasRandomValues, // Performance performance: () => this.hasPerformanceNow, hrtime: () => this.hasHighResolutionTime, // Network networkinfo: () => this.hasNetworkInformation, online: () => this.isOnline, // Workers webworker: () => typeof Worker !== 'undefined', serviceworker: () => typeof navigator !== 'undefined' && 'serviceWorker' in navigator, sharedworker: () => typeof SharedWorker !== 'undefined' }; const featureCheck = features[feature.toLowerCase()]; return featureCheck ? featureCheck() : false; } // Get comprehensive environment report getEnvironmentReport() { return { runtime: { isBrowser: this.isBrowser, isNodeJS: this.isNodeJS, isWorker: this.isWorker, isServiceWorker: this.isServiceWorker, isWebWorker: this.isWebWorker, isSharedWorker: this.isSharedWorker, isDeno: this.isDeno, isBun: this.isBun, isNativeScript: this.isNativeScript, isServer: this.isServer, isClient: this.isClient }, capabilities: { webrtc: this.hasWebRTC, dataChannel: this.hasDataChannel, getUserMedia: this.hasGetUserMedia, webSocket: this.hasWebSocket, localStorage: this.hasLocalStorage, sessionStorage: this.hasSessionStorage, indexedDB: this.hasIndexedDB, cookies: this.hasCookies, crypto: this.hasCrypto, randomValues: this.hasRandomValues, performance: this.hasPerformanceNow, networkInfo: this.hasNetworkInformation }, platform: this.getPlatformInfo(), browser: this.getBrowserInfo(), node: this.getNodeInfo(), nativescript: this.getNativeScriptInfo(), network: { online: this.isOnline, type: this.networkType } }; } // Static method for quick environment check static detect() { return new EnvironmentDetector(); } // Static method for single feature check static hasFeature(feature) { const detector = new EnvironmentDetector(); return detector.hasFeature(feature); } } // Export singleton instance for convenience export const environmentDetector = new EnvironmentDetector(); // Export individual detection functions for tree-shaking export const isBrowser = () => environmentDetector.isBrowser; export const isNodeJS = () => environmentDetector.isNodeJS; export const isWorker = () => environmentDetector.isWorker; export const isNativeScript = () => environmentDetector.isNativeScript; export const hasWebRTC = () => environmentDetector.hasWebRTC; export const hasWebSocket = () => environmentDetector.hasWebSocket; export const getEnvironmentReport = () => environmentDetector.getEnvironmentReport();