UNPKG

js-streaming

Version:

Unified streaming client for WebSocket, SSE, HTTP streaming, long-polling, HLS, and WebRTC.

463 lines (397 loc) 11.2 kB
export const javascript = ` // Install the library npm install js-streaming // Basic WebSocket example import { createStream } from 'js-streaming'; const stream = createStream({ type: 'websocket', url: 'wss://api.example.com/ws', autoReconnect: true, maxRetries: 5 }); // Listen for events stream.on('open', () => { console.log('✅ Connected!'); }); stream.on('message', (data) => { console.log('📨 Received:', data); }); stream.on('error', (error) => { console.error('❌ Error:', error); }); stream.on('close', () => { console.log('🔴 Disconnected'); }); // Connect and send data await stream.open(); stream.send({ message: 'Hello World!' }); `; export const jsx = ` import { useState, useEffect } from 'react'; import { createStream } from 'js-streaming'; function useStream(config) { const [isConnected, setIsConnected] = useState(false); const [messages, setMessages] = useState([]); const [error, setError] = useState(null); useEffect(() => { const stream = createStream(config); stream.on('open', () => { setIsConnected(true); setError(null); }); stream.on('message', (data) => { setMessages(prev => [...prev, data]); }); stream.on('error', (err) => { setError(err.message); }); stream.on('close', () => { setIsConnected(false); }); stream.open(); return () => { stream.close(); }; }, [config.url]); // Reconnect if URL changes return { isConnected, messages, error }; } // Usage in a Component function ChatComponent() { const { isConnected, messages, error } = useStream({ type: 'websocket', url: 'wss://chat.example.com', autoReconnect: true }); return ( <div> <div>Status: {isConnected ? '🟢 Connected' : '🔴 Disconnected'}</div> {error && <div className="error">{error}</div>} <div className="messages"> {messages.map((msg, i) => ( <div key={i}>{msg.text}</div> ))} </div> </div> ); } `; export const vue = ` import { ref, onMounted, onUnmounted } from 'vue'; import { createStream } from 'js-streaming'; export function useStream(config) { const isConnected = ref(false); const messages = ref([]); const error = ref(null); let stream; onMounted(() => { stream = createStream(config); stream.on('open', () => { isConnected.value = true; error.value = null; }); stream.on('message', (data) => { messages.value.push(data); }); stream.on('error', (err) => { error.value = err.message; }); stream.on('close', () => { isConnected.value = false; }); stream.open(); }); onUnmounted(() => { if (stream) { stream.close(); } }); const sendMessage = (message) => { if (stream && isConnected.value) { stream.send(message); } }; return { isConnected, messages, error, sendMessage }; } // Usage in a Component <template> <div class="chat"> <div class="status"> {{ isConnected ? '🟢 Connected' : '🔴 Disconnected' }} </div> <div v-if="error" class="error">{{ error }}</div> <div class="messages"> <div v-for="(msg, i) in messages" :key="i"> {{ msg.text }} </div> </div> <button @click="sendMessage({ text: 'Hello!' })"> Send Message </button> </div> </template> <script setup> const { isConnected, messages, error, sendMessage } = useStream({ type: 'websocket', url: 'wss://chat.example.com', autoReconnect: true }); </script> `; export const hls = ` import { createStream } from 'js-streaming'; import Hls from 'hls.js'; const videoStream = createStream({ type: 'hls', url: 'https://example.com/live/stream.m3u8', autoReconnect: true }); // Set up HLS video player const video = document.getElementById('videoPlayer'); let hls; videoStream.on('open', () => { if (Hls.isSupported()) { hls = new Hls(); hls.loadSource(videoStream.config.url); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { video.play(); }); hls.on(Hls.Events.ERROR, (event, data) => { if (data.fatal) { switch(data.type) { case Hls.ErrorTypes.NETWORK_ERROR: hls.startLoad(); break; case Hls.ErrorTypes.MEDIA_ERROR: hls.recoverMediaError(); break; default: videoStream.close(); break; } } }); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { // Native HLS support (Safari) video.src = videoStream.config.url; video.addEventListener('loadedmetadata', () => { video.play(); }); } }); videoStream.on('close', () => { if (hls) { hls.destroy(); } video.src = ''; }); // Start streaming videoStream.open(); `; export const webrtc = ` import { createStream } from 'js-streaming'; const rtcStream = createStream({ type: 'webrtc', url: 'wss://signaling.example.com', iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'turn:turn.example.com:3478', username: 'username', credential: 'password' } ], dataChannelConfig: { ordered: true, maxRetransmits: 3 } }); // Handle connection events rtcStream.on('open', () => { console.log('🌟 WebRTC connection established'); }); rtcStream.on('message', (data) => { console.log('📨 Received P2P data:', data); }); rtcStream.on('error', (error) => { console.error('❌ WebRTC error:', error); }); rtcStream.on('close', () => { console.log('🔴 WebRTC connection closed'); }); // Send data through data channel rtcStream.on('channelOpen', () => { rtcStream.send({ type: 'message', content: 'Hello P2P World!', timestamp: Date.now() }); }); // Connect to peer await rtcStream.open(); `; export const advanced = ` import { createStream, StreamProtocol } from 'js-streaming'; // Custom protocol implementation class CustomProtocol extends StreamProtocol { constructor(config) { super(config); this.reconnectAttempts = 0; } async connect() { // Custom connection logic try { await this.customConnect(); this.emit('open'); } catch (error) { this.handleError(error); } } async disconnect() { // Custom disconnection logic await this.customDisconnect(); this.emit('close'); } send(data) { // Custom send implementation if (!this.isConnected) { throw new Error('Not connected'); } this.customSend(data); } handleMessage(data) { // Custom message handling const parsed = this.customParse(data); this.emit('message', parsed); } handleError(error) { this.emit('error', error); if (this.config.autoReconnect && this.reconnectAttempts < this.config.maxRetries) { this.reconnectAttempts++; setTimeout(() => this.connect(), this.getBackoffDelay()); } } getBackoffDelay() { // Exponential backoff with jitter const baseDelay = 1000; const maxDelay = 30000; const exponential = Math.min( maxDelay, baseDelay * Math.pow(2, this.reconnectAttempts) ); return exponential + Math.random() * 1000; } } // Usage with custom protocol const stream = createStream({ type: 'custom', protocolClass: CustomProtocol, url: 'custom://example.com', autoReconnect: true, maxRetries: 5, customOptions: { encoding: 'utf-8', timeout: 5000 } }); // Advanced error handling stream.on('error', (error) => { if (error.code === 'TIMEOUT') { console.error('Connection timeout, attempting recovery...'); stream.reconnect(); } else if (error.code === 'RATE_LIMIT') { console.error('Rate limited, backing off...'); setTimeout(() => stream.reconnect(), 60000); } else { console.error('Fatal error:', error); stream.close(); } }); // Message filtering and transformation stream.on('message', (data) => { if (data.type === 'heartbeat') { return; // Ignore heartbeat messages } // Transform messages before processing const enriched = { ...data, timestamp: Date.now(), processed: true }; // Handle different message types switch (enriched.type) { case 'event': handleEvent(enriched); break; case 'state': updateState(enriched); break; case 'command': executeCommand(enriched); break; default: console.warn('Unknown message type:', enriched.type); } }); // Batch message sending with rate limiting class MessageBatcher { constructor(stream, options = {}) { this.stream = stream; this.queue = []; this.timeout = null; this.options = { maxBatchSize: options.maxBatchSize || 100, maxWaitTime: options.maxWaitTime || 1000, maxRequestsPerSecond: options.maxRequestsPerSecond || 10 }; } send(message) { this.queue.push(message); if (this.queue.length >= this.options.maxBatchSize) { this.flush(); } else if (!this.timeout) { this.timeout = setTimeout( () => this.flush(), this.options.maxWaitTime ); } } async flush() { if (this.timeout) { clearTimeout(this.timeout); this.timeout = null; } if (this.queue.length === 0) return; const batch = this.queue.splice(0, this.options.maxBatchSize); await this.stream.send({ type: 'batch', messages: batch, timestamp: Date.now() }); // Rate limiting await new Promise(resolve => setTimeout(resolve, 1000 / this.options.maxRequestsPerSecond) ); } } // Usage with batcher const batcher = new MessageBatcher(stream, { maxBatchSize: 50, maxWaitTime: 500, maxRequestsPerSecond: 5 }); // Send messages through batcher for (let i = 0; i < 1000; i++) { batcher.send({ id: i, type: 'event', data: { value: Math.random() } }); } `;