UNPKG

js-streaming

Version:

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

344 lines (334 loc) 9.2 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { createStream: () => createStream }); module.exports = __toCommonJS(index_exports); // src/core/Stream.ts var StreamCore = class { constructor(adapter, opts) { this.adapter = adapter; this.opts = opts; // نخليها loose جوه الكلاس this.listeners = {}; this.state = { status: "idle", retries: 0 }; } emit(event, ...args) { const cbs = this.listeners[event]; if (!cbs) return; for (const cb of cbs) { cb(...args); } } on(evt, cb) { if (!this.listeners[evt]) { this.listeners[evt] = []; } this.listeners[evt].push(cb); return () => this.off(evt, cb); } off(evt, cb) { const arr = this.listeners[evt]; if (!arr) return; this.listeners[evt] = arr.filter((fn) => fn !== cb); } async open() { this.state.status = "connecting"; this.emit("status", "connecting"); await this.adapter.open(); this.state.status = "open"; this.emit("status", "open"); } async close() { this.state.status = "closed"; this.emit("status", "closed"); await this.adapter.close(); } send(data) { this.adapter.send(data); } }; // src/adapters/websocket.ts function websocketAdapter(core, opts) { let socket = null; return { open: async () => { socket = new WebSocket(opts.url); socket.onopen = () => core.emit("open"); socket.onclose = () => core.emit("close"); socket.onerror = (err) => core.emit("error", err); socket.onmessage = (msg) => core.emit("message", msg.data); }, close: async () => { socket?.close(); }, send: (d) => { socket?.send(typeof d === "string" ? d : JSON.stringify(d)); }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/adapters/sse.ts function sseAdapter(core, opts) { let es = null; return { open: async () => { es = new EventSource(opts.url); es.onopen = () => core.emit("open"); es.onerror = (err) => core.emit("error", err); es.onmessage = (msg) => core.emit("message", msg.data); }, close: async () => { es?.close(); core.emit("close"); }, send: () => { throw new Error("SSE does not support send()"); }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/adapters/http.ts function httpStreamAdapter(core, opts) { let controller = null; return { open: async () => { controller = new AbortController(); const res = await fetch(opts.url, { signal: controller.signal }); if (!res.body) throw new Error("No response body"); const reader = res.body.getReader(); core.emit("open"); while (true) { const { done, value } = await reader.read(); if (done) break; core.emit("message", new TextDecoder().decode(value)); } core.emit("close"); }, close: async () => { controller?.abort(); core.emit("close"); }, send: () => { throw new Error("HTTP streaming does not support send()"); }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/adapters/longPolling.ts function longPollingAdapter(core, opts) { let active = false; const poll = async () => { while (active) { try { const res = await fetch(opts.url); const data = await res.json(); core.emit("message", data); } catch (err) { core.emit("error", err); } await new Promise((r) => setTimeout(r, opts.interval ?? 2e3)); } }; return { open: async () => { active = true; core.emit("open"); poll(); }, close: async () => { active = false; core.emit("close"); }, send: () => { throw new Error("Long polling does not support send()"); }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/adapters/hls.ts var import_hls = __toESM(require("hls.js"), 1); function hlsAdapter(core, opts) { let hls = null; return { open: async () => { if (!opts.video) { throw new Error("HLS requires a video element in options.video"); } if (opts.video.canPlayType("application/vnd.apple.mpegurl")) { opts.video.src = opts.url; } else if (import_hls.default.isSupported()) { hls = new import_hls.default(); hls.loadSource(opts.url); hls.attachMedia(opts.video); } else { core.emit("error", new Error("HLS not supported in this browser")); return; } core.emit("open"); core.emit("message", { url: opts.url }); }, close: async () => { if (hls) { hls.destroy(); hls = null; } if (opts.video) { opts.video.src = ""; } core.emit("close"); }, send: () => { throw new Error("HLS does not support send()"); }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/adapters/webrtc.ts function webrtcAdapter(core, opts) { let pc = null; let channel = null; return { open: async () => { pc = new RTCPeerConnection(); channel = pc.createDataChannel("data"); channel.onopen = () => core.emit("open"); channel.onclose = () => core.emit("close"); channel.onerror = (e) => core.emit("error", e); channel.onmessage = (e) => core.emit("message", e.data); }, close: async () => { pc?.close(); core.emit("close"); }, send: (d) => { if (channel && channel.readyState === "open") { channel.send(typeof d === "string" ? d : JSON.stringify(d)); } }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/adapters/socketio.ts var import_socket = require("socket.io-client"); function socketioAdapter(core, opts) { const socket = (0, import_socket.io)(opts.url, { reconnection: opts.autoReconnect ?? true, reconnectionAttempts: opts.maxRetries ?? 5 }); socket.on("connect", () => core.emit("open")); socket.on("disconnect", (reason) => core.emit("close", reason)); socket.on("connect_error", (err) => core.emit("error", err)); socket.onAny((event, data) => { core.emit("message", { event, data }); }); return { open: async () => { socket.connect(); }, close: async () => { socket.disconnect(); }, send: (d) => { socket.emit("message", d); }, on: core.on.bind(core), off: core.off.bind(core), get state() { return core.state; } }; } // src/index.ts function createStream(opts) { let adapter; const dummy = {}; const core = new StreamCore(dummy, opts); switch (opts.type) { case "websocket": adapter = websocketAdapter(core, opts); break; case "sse": adapter = sseAdapter(core, opts); break; case "http": adapter = httpStreamAdapter(core, opts); break; case "long-polling": adapter = longPollingAdapter(core, opts); break; case "hls": adapter = hlsAdapter(core, opts); break; case "webrtc": adapter = webrtcAdapter(core, opts); break; case "socketio": adapter = socketioAdapter(core, opts); break; default: throw new Error(`Unknown stream type: ${opts.type}`); } core.adapter = adapter; return adapter; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createStream }); //# sourceMappingURL=index.cjs.map