UNPKG

@ch1/rpc-web-socket

Version:

JavaScript Remote Procedure Call (RPC) - Web Socket Adapter

152 lines (147 loc) 4.83 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var rpc = require('@ch1/rpc'); /** * This module uses `JSON.stringify` and `JSON.parse` for a number of reasons: * * - I'm too lazy to figure out an efficient `ArrayBuffer` approach, that said * I can imagine it easy to make a hybrid... * - Historically the library used them out of paranoia and sloth (detecting * browser object clone support) * - Because apparently `JSON` is actually reasonable: * https://nolanlawson.com/2016/02/29/high-performance-web-worker-messages/ */ function configureNativeSocketOnMethod(config) { config.on = (callback) => { const handler = (data) => { callback(JSON.parse(data.data)); }; config.socket.addEventListener('message', handler); return () => config.socket.removeEventListener('message', handler); }; } function configureWsSocketOnMethod(config) { config.on = (callback) => { const handler = (data) => { callback(JSON.parse(data)); }; config.socket.on('message', handler); return () => config.socket.removeEventListener('message', handler); }; } function configureOnEmit(config) { if (!config.socket) { throw new TypeError('rpc-web-socket: socket interface required'); } if (typeof config.socket.on !== 'function') { if (typeof config.socket.addEventListener !== 'function') { throw new TypeError('rpc-web-socket: socket must have an on method'); } configureNativeSocketOnMethod(config); } else { configureWsSocketOnMethod(config); } if (typeof config.socket.send !== 'function') { throw new TypeError('rpc-web-socket: socket must have a send method'); } config.emit = (data) => { config.socket.send(JSON.stringify(data)); }; } function configureNativeSocket(config, remote, remoteDesc) { let resolve; let reject; let rpc$$1; const earlyDestroys = []; const expose = { onDestroy: (...args) => { let destroyed = false; const destroy = (reason) => { destroyed = true; desc.onDestroy(reason); }; const desc = { isValid: () => { if (destroyed) { return false; } else { return true; } }, args, onDestroy: reason => { }, }; earlyDestroys.push(desc); return destroy; }, ready: new Promise((res, rej) => { resolve = res; reject = rej; }), }; // @todo awkard... this will need a refactor config.socket.addEventListener('close', () => { if (rpc$$1) { rpc$$1.destroy('rpc: web-socket closed'); } else { console.warn('rpc: web-socket error: rpc still not defined (close)'); } }); // @todo awkard... this will need a refactor config.socket.addEventListener('error', error => { if (rpc$$1) { rpc$$1.destroy('rpc: web-socket error: native: ' + error.message); } else { console.warn('rpc: web-socket error: rpc still not defined (error)'); } }); config.socket.addEventListener('open', () => { rpc$$1 = rpc.create(config, remote, remoteDesc); earlyDestroys.forEach(desc => { if (desc.isValid()) { desc.onDestroy = rpc$$1.onDestroy(...desc.args); } }); rpc$$1.ready .then(() => { Object.keys(rpc$$1).forEach(key => { expose[key] = rpc$$1[key]; }); resolve(); }) .catch(reject); }); return expose; } function configureWsSocket(config, remote, remoteDesc) { const rpc$$1 = rpc.create(config, remote, remoteDesc); const interval = setInterval(() => { if (config.socket.isAlive === false) { config.socket.terminate(); } config.socket.isAlive = false; config.socket.ping(); }, config.pingDelay || 10000); config.socket.isAlive = true; config.socket.on('pong', () => (config.socket.isAlive = true)); const destroy = rpc$$1.destroy; rpc$$1.destroy = () => { clearInterval(interval); return destroy(); }; return rpc$$1; } function create(config, remote, remoteDesc) { configureOnEmit(config); if (typeof WebSocket !== 'undefined') { return configureNativeSocket(config, remote, remoteDesc); } else { return configureWsSocket(config, remote, remoteDesc); } } exports.create = create;