UNPKG

ive-connect

Version:

A universal haptic device control library for interactive experiences

108 lines (107 loc) 3.57 kB
"use strict"; /** * Minimal Buttplug v4 WebSocket transport */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ButtplugWs = void 0; const PROTOCOL_VERSION_MAJOR = 4; const PROTOCOL_VERSION_MINOR = 0; class ButtplugWs { constructor(onMessage) { this.ws = null; this.msgId = 0; this.pending = new Map(); this.pingTimer = null; this.onMessage = onMessage; } get connected() { var _a; return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN; } async open(url, clientName) { await new Promise((resolve, reject) => { try { this.ws = new WebSocket(url); } catch (err) { reject(err); return; } this.ws.onopen = async () => { try { const info = await this.send('RequestServerInfo', { ClientName: clientName, ProtocolVersionMajor: PROTOCOL_VERSION_MAJOR, ProtocolVersionMinor: PROTOCOL_VERSION_MINOR, }); if (info.MaxPingTime && info.MaxPingTime > 0) { this.pingTimer = setInterval(() => this.send('Ping', {}).catch(() => { }), Math.floor(info.MaxPingTime / 2)); } await this.send('RequestDeviceList', {}); resolve(); } catch (err) { reject(err); } }; this.ws.onerror = () => reject(new Error(`WS failed: ${url}`)); this.ws.onclose = () => this.cleanup(); this.ws.onmessage = (e) => this.handleRaw(e.data); }); } async send(type, payload) { return new Promise((resolve, reject) => { if (!this.connected) { reject(new Error('Not connected')); return; } const id = ++this.msgId; this.pending.set(id, { resolve, reject }); this.ws.send(JSON.stringify([{ [type]: { Id: id, ...payload } }])); }); } close() { var _a; if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) this.ws.close(); this.cleanup(); } handleRaw(data) { let msgs; try { msgs = JSON.parse(data); } catch (_a) { return; } for (const msg of msgs) { const type = Object.keys(msg)[0]; const payload = msg[type]; const id = payload === null || payload === void 0 ? void 0 : payload.Id; if (id !== undefined && this.pending.has(id)) { if (type === 'Error') { this.pending .get(id) .reject(new Error(payload.ErrorMessage)); } else { this.pending.get(id).resolve(payload); } this.pending.delete(id); } this.onMessage(type, payload); } } cleanup() { if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; } for (const p of this.pending.values()) { p.reject(new Error('Connection closed')); } this.pending.clear(); this.ws = null; } } exports.ButtplugWs = ButtplugWs;