@julesl23/s5js
Version:
Enhanced TypeScript SDK for S5 decentralized storage with path-based API, media processing, and directory utilities
317 lines • 10.9 kB
JavaScript
/**
* Browser compatibility detection and strategy selection
*/
export class BrowserCompat {
static capabilities;
static browserInfo;
/**
* Reset cached capabilities (mainly for testing)
*/
static resetCache() {
this.capabilities = undefined;
this.browserInfo = undefined;
}
/**
* Check browser capabilities
*/
static async checkCapabilities() {
if (this.capabilities) {
return this.capabilities;
}
const caps = {
webAssembly: false,
webAssemblyStreaming: false,
sharedArrayBuffer: false,
webWorkers: false,
offscreenCanvas: false,
webP: false,
avif: false,
createImageBitmap: false,
webGL: false,
webGL2: false,
memoryLimit: 512, // Default 512MB
performanceAPI: false,
memoryInfo: false
};
// Check WebAssembly support
try {
if (typeof WebAssembly === 'object' && WebAssembly !== null) {
caps.webAssembly = true;
caps.webAssemblyStreaming = typeof WebAssembly.instantiateStreaming === 'function';
}
}
catch {
// WebAssembly not supported
}
// Check SharedArrayBuffer (may be disabled due to Spectre mitigations)
try {
if (typeof SharedArrayBuffer !== 'undefined') {
new SharedArrayBuffer(1);
caps.sharedArrayBuffer = true;
}
}
catch {
// SharedArrayBuffer not supported or disabled
}
// Check Web Workers
caps.webWorkers = typeof Worker !== 'undefined';
// Check OffscreenCanvas
caps.offscreenCanvas = typeof OffscreenCanvas !== 'undefined';
// Check createImageBitmap
caps.createImageBitmap = typeof createImageBitmap === 'function';
// Check WebGL support
if (typeof document !== 'undefined') {
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
caps.webGL = !!gl;
const gl2 = canvas.getContext('webgl2');
caps.webGL2 = !!gl2;
}
catch {
// WebGL not supported
}
}
// Check Performance API
caps.performanceAPI = typeof performance !== 'undefined' &&
typeof performance.now === 'function';
// Check memory constraints
caps.memoryLimit = this.detectMemoryLimit();
caps.memoryInfo = typeof performance !== 'undefined' && !!performance.memory;
// Check image format support
if (this.isBrowserEnvironment()) {
caps.webP = await this.checkImageFormatSupport('image/webp');
caps.avif = await this.checkImageFormatSupport('image/avif');
}
this.capabilities = caps;
return caps;
}
/**
* Check if a specific image format is supported
*/
static checkImageFormatSupport(mimeType) {
return new Promise((resolve) => {
// In Node.js environment, return false
if (!this.isBrowserEnvironment()) {
resolve(false);
return;
}
const img = new Image();
img.onload = () => resolve(true);
img.onerror = () => resolve(false);
// 1x1 pixel test images
if (mimeType === 'image/webp') {
// Minimal WebP image
img.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
}
else if (mimeType === 'image/avif') {
// Minimal AVIF image
img.src = 'data:image/avif;base64,AAAAHGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZgAAAPBtZXRhAAAA';
}
else {
resolve(false);
}
});
}
/**
* Detect available memory limit
*/
static detectMemoryLimit() {
// In Node.js, use process.memoryUsage
if (this.isNodeEnvironment()) {
try {
const usage = process.memoryUsage();
return Math.floor(usage.heapTotal / 1048576); // Convert to MB
}
catch {
return 512; // Default
}
}
// In browser, try to use performance.memory (Chrome only)
if (typeof performance !== 'undefined' && performance.memory) {
const memory = performance.memory;
if (memory.jsHeapSizeLimit) {
return Math.floor(memory.jsHeapSizeLimit / 1048576); // Convert to MB
}
}
// Try to estimate based on navigator.deviceMemory (Chrome only)
if (typeof navigator !== 'undefined' && navigator.deviceMemory) {
return navigator.deviceMemory * 1024; // Convert GB to MB
}
// Default fallback
return 512; // 512MB default
}
/**
* Select optimal processing strategy based on capabilities
*/
static selectProcessingStrategy(caps) {
// Consider memory constraints - avoid WASM with very low memory
const lowMemory = caps.memoryLimit < 512;
// Best: WASM in Web Worker
if (caps.webAssembly && caps.webWorkers && !lowMemory) {
return 'wasm-worker';
}
// Good: WASM in main thread
if (caps.webAssembly && !lowMemory) {
return 'wasm-main';
}
// OK: Canvas in Web Worker
if (caps.webWorkers && caps.offscreenCanvas) {
return 'canvas-worker';
}
// Fallback: Canvas in main thread
return 'canvas-main';
}
/**
* Get browser information
*/
static getBrowserInfo() {
if (this.browserInfo) {
return this.browserInfo;
}
const userAgent = this.getUserAgent();
this.browserInfo = this.parseBrowserInfo(userAgent);
return this.browserInfo;
}
/**
* Parse browser info from user agent string
*/
static parseBrowserInfo(userAgent) {
const info = {
name: 'Unknown',
version: '0',
platform: 'Unknown',
isMobile: false
};
// Detect mobile
info.isMobile = /Mobile|Android|iPhone|iPad|iPod/i.test(userAgent);
// Detect platform - iOS first since it contains "Mac OS X" in user agent
if (/iPhone|iPad|iPod/i.test(userAgent)) {
info.platform = 'iOS';
}
else if (/Android/i.test(userAgent)) {
info.platform = 'Android';
}
else if (/Mac OS X/i.test(userAgent)) {
info.platform = 'macOS';
}
else if (/Windows/i.test(userAgent)) {
info.platform = 'Windows';
}
else if (/Linux/i.test(userAgent)) {
info.platform = 'Linux';
}
// Detect browser - order matters!
if (/Edg\/(\d+\.\d+\.\d+\.\d+)/i.test(userAgent)) {
info.name = 'Edge';
info.version = RegExp.$1;
}
else if (/Chrome\/(\d+\.\d+\.\d+\.\d+)/i.test(userAgent)) {
info.name = 'Chrome';
info.version = RegExp.$1;
}
else if (/Firefox\/(\d+\.\d+)/i.test(userAgent)) {
info.name = 'Firefox';
info.version = RegExp.$1;
}
else if (/Version\/(\d+\.\d+\.\d+).*Safari/i.test(userAgent)) {
info.name = 'Safari';
info.version = RegExp.$1;
}
else if (/Safari/i.test(userAgent)) {
info.name = 'Safari';
// Try to extract version from Version/ tag
const versionMatch = userAgent.match(/Version\/(\d+\.\d+)/);
if (versionMatch) {
info.version = versionMatch[1];
}
}
return info;
}
/**
* Get user agent string
*/
static getUserAgent() {
if (typeof navigator !== 'undefined' && navigator.userAgent) {
return navigator.userAgent;
}
return '';
}
/**
* Get optimization recommendations based on capabilities
*/
static getOptimizationRecommendations(caps) {
const recommendations = [];
if (!caps.webAssembly) {
recommendations.push('Consider upgrading to a browser with WASM support for better performance');
}
if (!caps.webWorkers) {
recommendations.push('Web Workers are not available - processing will block the main thread');
}
if (!caps.sharedArrayBuffer) {
recommendations.push('SharedArrayBuffer is disabled - parallel processing capabilities are limited');
}
if (caps.memoryLimit < 512) {
recommendations.push('Low memory detected - consider closing other applications');
}
if (!caps.webP) {
recommendations.push('WebP format not supported - using fallback formats');
}
if (!caps.avif) {
recommendations.push('AVIF format not supported - using older formats');
}
if (!caps.offscreenCanvas) {
recommendations.push('OffscreenCanvas not available - worker-based rendering is limited');
}
return recommendations;
}
/**
* Get preferred image formats based on support
*/
static getPreferredImageFormats(caps) {
const formats = [];
// Add in order of preference
if (caps.avif) {
formats.push('avif');
}
if (caps.webP) {
formats.push('webp');
}
// Always include fallbacks
formats.push('jpeg');
formats.push('png');
return formats;
}
/**
* Check if running in Node.js environment
*/
static isNodeEnvironment() {
return typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;
}
/**
* Check if running in browser environment
*/
static isBrowserEnvironment() {
return typeof window !== 'undefined' &&
typeof document !== 'undefined' &&
!this.isNodeEnvironment();
}
/**
* Check if running in service worker context
*/
static isServiceWorkerContext() {
return typeof self !== 'undefined' &&
'ServiceWorkerGlobalScope' in self;
}
/**
* Check if running in web worker context
*/
static isWebWorkerContext() {
return typeof self !== 'undefined' &&
typeof globalThis.importScripts === 'function' &&
!this.isServiceWorkerContext();
}
}
//# sourceMappingURL=browser.js.map