UNPKG

@graffy/client

Version:

Graffy client library for the browser.

192 lines (150 loc) 5.01 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = Socket; var _keys = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/keys")); var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout")); var _slice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/slice")); var _now = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/date/now")); var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat")); var _bind = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/bind")); var _common = require("@graffy/common"); var _debug = _interopRequireDefault(require("debug")); var _context; var log = (0, _debug.default)('graffy:client:socket'); // eslint-disable-next-line no-console log.log = (0, _bind.default)(_context = console.log).call(_context, console); var MIN_DELAY = 1000; var MAX_DELAY = 300000; var DELAY_GROWTH = 1.5; var INTERVAL = 2000; var PING_TIMEOUT = 40000; // Make this greater than server interval. var RESET_TIMEOUT = 10000; function Socket(url, _temp) { var _ref = _temp === void 0 ? {} : _temp, onUnhandled = _ref.onUnhandled, onStatusChange = _ref.onStatusChange; var handlers = {}; var buffer = []; var isOpen = false; var isConnecting = false; var socket; var lastAlive; var lastAttempt; var attempts = 0; var connectTimer; var aliveTimer; function start(params, callback) { var _context2; var id = (0, _common.makeId)(); var request = (0, _concat.default)(_context2 = [id]).call(_context2, params); handlers[id] = { request: request, callback: callback }; if (isAlive()) send(request); return id; } function stop(id, params) { var _context3; delete handlers[id]; if (params) send((0, _concat.default)(_context3 = [id]).call(_context3, params)); } function connect() { log('Trying to connect to', url); isOpen = false; isConnecting = true; lastAttempt = (0, _now.default)(); attempts++; socket = new WebSocket(url); socket.onmessage = received; socket.onerror = closed; socket.onclose = closed; socket.onopen = opened; } function received(event) { var _deserialize = (0, _common.deserialize)(event.data), id = _deserialize[0], data = (0, _slice.default)(_deserialize).call(_deserialize, 1); setAlive(); if (id === ':ping') { send([':pong']); } else if (handlers[id]) { var _handlers$id; (_handlers$id = handlers[id]).callback.apply(_handlers$id, data); } else { var _context4; // We received an unexpected push. onUnhandled && onUnhandled.apply(void 0, (0, _concat.default)(_context4 = [id]).call(_context4, data)); } } function closed(_event) { log('Closed'); if (isOpen && onStatusChange) onStatusChange(false); var wasOpen = isOpen; isOpen = false; isConnecting = false; lastAttempt = (0, _now.default)(); if (wasOpen && !attempts) { // Quick reconnect path if we previously had a stable connection. connect(); return; } maybeConnect(); } function maybeConnect() { var connDelay = lastAttempt + Math.min(MAX_DELAY, MIN_DELAY * Math.pow(DELAY_GROWTH, attempts)) - (0, _now.default)(); log('Will reconnect in', connDelay, 'ms'); if (connDelay <= 0) { connect(); return; } clearTimeout(connectTimer); connectTimer = (0, _setTimeout2.default)(connect, connDelay); } function opened() { log('Connected', buffer.length, (0, _keys.default)(handlers).length); isOpen = true; isConnecting = false; lastAttempt = (0, _now.default)(); setAlive(); if (onStatusChange) onStatusChange(true); for (var id in handlers) { send(handlers[id].request); } while (buffer.length) { send(buffer.shift()); } } function setAlive() { lastAlive = (0, _now.default)(); log('Set alive', lastAlive - lastAttempt); if (lastAlive - lastAttempt > RESET_TIMEOUT) attempts = 0; } function isAlive() { log('Liveness check', isOpen ? 'open' : 'closed', (0, _now.default)() - lastAlive); clearTimeout(aliveTimer); aliveTimer = (0, _setTimeout2.default)(isAlive, INTERVAL); if (!isOpen) { if (!isConnecting) maybeConnect(); return false; } if ((0, _now.default)() - lastAlive < PING_TIMEOUT) return true; socket.close(); return false; } function send(req) { if (isAlive()) { socket.send((0, _common.serialize)(req)); } else { buffer.push(req); } } connect(); aliveTimer = (0, _setTimeout2.default)(isAlive, INTERVAL); return { start: start, stop: stop, isAlive: isAlive }; } module.exports = exports.default;