UNPKG

capacitor-cors-bypass-enhanced

Version:

Enhanced Capacitor plugin for CORS bypass with HTTP/2, HTTP/3, gRPC, GraphQL, file operations, and advanced networking features. Modular TypeScript definitions for better maintainability.

199 lines (172 loc) 5.68 kB
import type { SSEOptions, SSEConnectionOptions, SSEConnection, SSEMessageEvent, SSEConnectionChangeEvent, } from '../definitions'; import { isCrossOrigin } from './utils'; /** * SSE Manager * Handles Server-Sent Events connections with CORS bypass */ export class SSEManager { private proxyServerUrl: string | null; private sseConnections = new Map<string, EventSource>(); private connectionCounter = 0; private notifyListeners: (eventName: string, data: any) => void; constructor( proxyServerUrl: string | null, notifyListeners: (eventName: string, data: any) => void ) { this.proxyServerUrl = proxyServerUrl; this.notifyListeners = notifyListeners; } /** * Start listening to Server-Sent Events (legacy method) */ async startSSE(options: SSEOptions): Promise<{ connectionId: string }> { const connectionId = `sse_${++this.connectionCounter}`; const { url, headers = {}, withCredentials = false, reconnectTimeout = 3000 } = options; // Use proxy server for SSE if available and cross-origin let sseUrl = url; if (this.proxyServerUrl && isCrossOrigin(url)) { console.log(`🔧 Using SSE proxy for: ${url}`); sseUrl = `${this.proxyServerUrl}/sse-proxy/${encodeURIComponent(url)}`; } const eventSource = new EventSource(sseUrl); this.sseConnections.set(connectionId, eventSource); eventSource.onopen = () => { this.notifyListeners('sseOpen', { connectionId, status: 'connected', }); }; eventSource.onmessage = (event) => { this.notifyListeners('sseMessage', { connectionId, type: 'message', data: event.data, id: event.lastEventId, } as SSEMessageEvent); }; eventSource.onerror = () => { this.notifyListeners('sseError', { connectionId, error: 'Connection error', }); }; return { connectionId }; } /** * Stop listening to Server-Sent Events */ async stopSSE(options: { connectionId: string }): Promise<void> { const { connectionId } = options; const connection = this.sseConnections.get(connectionId); if (connection) { connection.close(); this.sseConnections.delete(connectionId); this.notifyListeners('sseClose', { connectionId, status: 'disconnected', }); } } /** * Create a Server-Sent Events connection with reconnection support */ async createSSEConnection(options: SSEConnectionOptions): Promise<SSEConnection> { const connectionId = `sse_${++this.connectionCounter}`; const { url, headers = {}, reconnect = {} } = options; const { enabled: reconnectEnabled = true, initialDelay = 1000, maxDelay = 30000, maxAttempts = 10, } = reconnect; let retryCount = 0; let retryDelay = initialDelay; const createConnection = () => { // Use proxy server for SSE if available and cross-origin let sseUrl = url; if (this.proxyServerUrl && isCrossOrigin(url)) { console.log(`🔧 Using SSE proxy for: ${url}`); sseUrl = `${this.proxyServerUrl}/sse-proxy/${encodeURIComponent(url)}`; } const eventSource = new EventSource(sseUrl); this.sseConnections.set(connectionId, eventSource); eventSource.onopen = () => { retryCount = 0; retryDelay = initialDelay; this.notifyListeners('sseConnectionChange', { connectionId, status: 'connected', } as SSEConnectionChangeEvent); }; eventSource.onmessage = (event) => { this.notifyListeners('sseMessage', { connectionId, type: 'message', data: event.data, id: event.lastEventId, } as SSEMessageEvent); }; eventSource.onerror = () => { this.notifyListeners('sseConnectionChange', { connectionId, status: 'error', error: 'Connection error', } as SSEConnectionChangeEvent); if (reconnectEnabled && retryCount < maxAttempts) { setTimeout(() => { retryCount++; retryDelay = Math.min(retryDelay * 2, maxDelay); eventSource.close(); createConnection(); }, retryDelay); } else { this.sseConnections.delete(connectionId); } }; // Add custom event listeners eventSource.addEventListener('error', (event) => { this.notifyListeners('sseMessage', { connectionId, type: 'error', data: 'Connection error', } as SSEMessageEvent); }); }; this.notifyListeners('sseConnectionChange', { connectionId, status: 'connecting', } as SSEConnectionChangeEvent); createConnection(); return { connectionId, status: 'connecting', }; } /** * Close an SSE connection */ async closeSSEConnection(options: { connectionId: string }): Promise<void> { const { connectionId } = options; const connection = this.sseConnections.get(connectionId); if (connection) { connection.close(); this.sseConnections.delete(connectionId); this.notifyListeners('sseConnectionChange', { connectionId, status: 'disconnected', } as SSEConnectionChangeEvent); } } /** * Get all active SSE connections */ getSSEConnections(): Map<string, EventSource> { return this.sseConnections; } }