UNPKG

rtlayer-client

Version:

Make your app fun to use by adding realtime features in just few lines of code and you don't event have to handle any extra infrastructure.

276 lines (275 loc) 13.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; Object.defineProperty(exports, "__esModule", { value: true }); var BASE_URL = "wss://ws.rtlayer.com"; var MessageQueue = /** @class */ (function () { function MessageQueue() { this.queue = []; } MessageQueue.prototype.push = function (message) { this.queue.push(message); }; MessageQueue.prototype.pop = function () { return this.queue.shift(); }; MessageQueue.prototype.size = function () { return this.queue.length; }; MessageQueue.prototype.messages = function () { return __asyncGenerator(this, arguments, function messages_1() { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!true) return [3 /*break*/, 6]; if (!(this.size() > 0)) return [3 /*break*/, 3]; return [4 /*yield*/, __await(this.pop())]; case 1: return [4 /*yield*/, _a.sent()]; case 2: _a.sent(); return [3 /*break*/, 5]; case 3: return [4 /*yield*/, __await(delay(100))]; case 4: _a.sent(); _a.label = 5; case 5: return [3 /*break*/, 0]; case 6: return [2 /*return*/]; } }); }); }; return MessageQueue; }()); var WebSocketClient = /** @class */ (function () { function WebSocketClient(org, service, token, retryInterval) { if (retryInterval === void 0) { retryInterval = 5000; } this.ws = null; this.eventListeners = {}; this.isGloballySubscribed = false; // True if listening for "*" events this.activeChannels = new Set(); this.queue = new MessageQueue(); this.url = "".concat(BASE_URL, "/").concat(org, "/").concat(service, "/?token=").concat(token); this.retryInterval = retryInterval; this.token = token; this.connect(); } WebSocketClient.prototype.connect = function () { var _this = this; if (!this.ws || this.ws.readyState === WebSocket.CLOSED) { this.ws = new WebSocket(this.url); this.ws.onopen = function () { return __awaiter(_this, void 0, void 0, function () { var _a, _b, _c, message, e_1_1; var _this = this; var _d, e_1, _e, _f; var _g; return __generator(this, function (_h) { switch (_h.label) { case 0: this.emit('open', null); // Rejoin active channels on reconnect this.activeChannels.forEach(function (channel) { return _this.subscribe(channel); }); _h.label = 1; case 1: _h.trys.push([1, 7, 8, 13]); _a = true, _b = __asyncValues(this.queue.messages()); _h.label = 2; case 2: return [4 /*yield*/, _b.next()]; case 3: if (!(_c = _h.sent(), _d = _c.done, !_d)) return [3 /*break*/, 6]; _f = _c.value; _a = false; message = _f; if (!(((_g = this.ws) === null || _g === void 0 ? void 0 : _g.readyState) === WebSocket.OPEN && message)) return [3 /*break*/, 5]; this.ws.send(message); return [4 /*yield*/, delay(10)]; case 4: _h.sent(); _h.label = 5; case 5: _a = true; return [3 /*break*/, 2]; case 6: return [3 /*break*/, 13]; case 7: e_1_1 = _h.sent(); e_1 = { error: e_1_1 }; return [3 /*break*/, 13]; case 8: _h.trys.push([8, , 11, 12]); if (!(!_a && !_d && (_e = _b.return))) return [3 /*break*/, 10]; return [4 /*yield*/, _e.call(_b)]; case 9: _h.sent(); _h.label = 10; case 10: return [3 /*break*/, 12]; case 11: if (e_1) throw e_1.error; return [7 /*endfinally*/]; case 12: return [7 /*endfinally*/]; case 13: return [2 /*return*/]; } }); }); }; this.ws.onmessage = function (event) { var data = event.data; try { // If valid JSON, emit the message on the channel data = JSON.parse(data); if (data === null || data === void 0 ? void 0 : data.channel) { _this.emit(data.channel, data.message); data = data.message; } else { data = event.data; } } catch (error) { data = data; } _this.emit('*', data); _this.emit('message', data); }; this.ws.onclose = function () { _this.emit('close', null); setTimeout(function () { // Retry connection _this.connect(); }, _this.retryInterval); }; } }; WebSocketClient.prototype.send = function (message) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(message); } }; WebSocketClient.prototype.subscribe = function (channel) { this.activeChannels.add(channel); var message = { action: 'join', id: channel }; this.queue.push(JSON.stringify(message)); }; WebSocketClient.prototype.unsubscribe = function (channel) { this.activeChannels.delete(channel); var message = { action: 'leave', id: channel }; this.queue.push(JSON.stringify(message)); }; WebSocketClient.prototype.close = function () { if (this.ws) { this.ws.close(); } }; WebSocketClient.prototype.on = function (channel, callback) { var _this = this; if (channel === '*') this.isGloballySubscribed = true; if (!this.eventListeners[channel]) { this.eventListeners[channel] = []; } if (channel !== '*' && this.eventListeners[channel].length === 0) this.subscribe(channel); this.eventListeners[channel].push(callback); return { remove: function () { _this.removeListener(channel, callback); } }; }; WebSocketClient.prototype.removeListener = function (channel, listener) { if (channel === '*') { this.isGloballySubscribed = false; // Unsubscribe all channels which have no listeners for (var channel_1 in this.eventListeners) { if (this.eventListeners[channel_1].length === 0) { this.unsubscribe(channel_1); } } } var listeners = this.eventListeners[channel] || []; var index = listeners.indexOf(listener); if (index !== -1) { listeners.splice(index, 1); } this.eventListeners[channel] = listeners; // Unsubscribe if no listeners if (listeners.length === 0 && !this.isGloballySubscribed) this.unsubscribe(channel); }; WebSocketClient.prototype.emit = function (event, data) { var listeners = this.eventListeners[event]; if (listeners) { for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) { var listener = listeners_1[_i]; listener(data); } } }; return WebSocketClient; }()); var cache = new Map(); function RTLayerClient(org, service, token, retryInterval) { if (retryInterval === void 0) { retryInterval = 5000; } var key = "".concat(org, "/").concat(service); if (cache.has(key)) { return cache.get(key); } var client = new WebSocketClient(org, service, token, retryInterval); cache.set(key, client); return client; } exports.default = RTLayerClient; function delay(ms) { if (ms === void 0) { ms = 1000; } return new Promise(function (resolve) { return setTimeout(resolve, ms); }); }