/** * Utility Helper Functions */ import type { CapturedRequest } from '../types'; /** * Check if ServiceWorker is supported */ export function isServiceWorkerSupported(): boolean { return 'serviceWorker' in navigator; } /** * Check if running in secure context (HTTPS or localhost) */ export function isSecureContext(): boolean { return location.protocol === 'https:' || location.hostname === 'localhost' || location.hostname === '127.0.0.1'; } /** * Generate mock file path from URL */ export function generateMockFilePath(url: string): string { try { const urlObj = new URL(url); let path = urlObj.pathname; // Remove leading slash if (path.startsWith('/')) { path = path.substring(1); } // Remove service prefixes const servicePrefixes = ['microgrids-service', 'digital-portal', 'old-microgrids-service']; for (const prefix of servicePrefixes) { if (path.startsWith(prefix + '/')) { path = path.substring(prefix.length + 1); } } // Replace slashes with underscores path = path.replace(/\//g, '_'); // Add .json suffix return `${path}.json`; } catch (error) { console.error('[SwMock] Failed to generate mock file path:', error); return 'unknown.json'; } } /** * Export captured requests as JSON file */ export function exportCapturedRequests(requests: CapturedRequest[]): void { if (requests.length === 0) { console.warn('[SwMock] No captured requests to export'); return; } const dataStr = JSON.stringify(requests, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `sw-mock-captured-requests-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); console.log(`[SwMock] Exported ${requests.length} captured requests`); } /** * Deep clone object */ export function deepClone(obj: T): T { if (obj === null || typeof obj !== 'object') { return obj; } if (obj instanceof Date) { return new Date(obj.getTime()) as unknown as T; } if (obj instanceof Array) { return obj.map(item => deepClone(item)) as unknown as T; } if (typeof obj === 'object') { const cloned = {} as T; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key]); } } return cloned; } return obj; } /** * Debounce function */ export function debounce any>( func: T, wait: number ): (...args: Parameters) => void { let timeout: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } /** * Throttle function */ export function throttle any>( func: T, limit: number ): (...args: Parameters) => void { let inThrottle: boolean; return (...args: Parameters) => { if (!inThrottle) { func(...args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * Format bytes to human readable string */ export function formatBytes(bytes: number, decimals = 2): string { if (bytes === 0) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } /** * Format duration to human readable string */ export function formatDuration(ms: number): string { if (ms < 1000) { return `${ms}ms`; } const seconds = Math.floor(ms / 1000); if (seconds < 60) { return `${seconds}s`; } const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; if (minutes < 60) { return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`; } const hours = Math.floor(minutes / 60); const remainingMinutes = minutes % 60; return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`; } /** * Generate random ID */ export function generateId(length = 8): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } /** * Check if URL matches patterns */ export function matchesPatterns(url: string, patterns: string[]): boolean { return patterns.some(pattern => { if (pattern.includes('*')) { // Convert glob pattern to regex const regexPattern = pattern .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') .replace(/\\\*/g, '.*'); return new RegExp(regexPattern).test(url); } return url.includes(pattern); }); } /** * Validate URL */ export function isValidUrl(string: string): boolean { try { new URL(string); return true; } catch (_) { return false; } } /** * Get environment info */ export function getEnvironmentInfo() { return { userAgent: navigator.userAgent, platform: navigator.platform, language: navigator.language, cookieEnabled: navigator.cookieEnabled, onLine: navigator.onLine, serviceWorkerSupported: isServiceWorkerSupported(), secureContext: isSecureContext(), url: location.href, origin: location.origin, timestamp: new Date().toISOString() }; }