golos-lib-js
Version:
Golos-js the JavaScript library with API for GOLOS blockchain
163 lines (162 loc) • 5.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _detectNode = _interopRequireDefault(require("detect-node"));
var _debug = _interopRequireDefault(require("debug"));
var _base = _interopRequireDefault(require("./base"));
var _promisify = require("../../promisify");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const cbMethods = ['set_block_applied_callback', 'set_pending_transaction_callback', 'set_callback', 'subscribe_to_market'];
let WebSocket;
if (_detectNode.default) {
WebSocket = require('ws'); // eslint-disable-line global-require
} else if (typeof window !== 'undefined') {
WebSocket = window.WebSocket;
} else {
throw new Error('Couldn\'t decide on a `WebSocket` class');
}
const debugProtocol = (0, _debug.default)('golos:protocol');
const debugWs = (0, _debug.default)('golos:ws');
class WsTransport extends _base.default {
constructor() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
super(Object.assign({
id: 0
}, options));
this.isOpen = false;
}
start() {
if (this.startP) {
return this.startP;
}
let startP;
startP = new Promise((resolve, reject) => {
if (startP !== this.startP) return;
const url = this.getURL();
this.ws = new WebSocket(url);
const releaseOpen = this.listenTo(this.ws, 'open', () => {
debugWs('Opened WS connection with', url);
this.isOpen = true;
releaseOpen();
resolve();
});
const errHandler = () => {
const wasOpen = this.isOpen;
this.isOpen = false;
delete this.ws;
this.stop();
const err = new Error('The WS connection was closed before this operation was made');
if (!wasOpen) {
reject(err);
}
for (let [id, val] of Object.entries(this.requests)) {
delete this.requests[id];
val.reject(err);
}
for (let [id, val] of Object.entries(this.callbacks)) {
delete this.callbacks[id];
val.cb(err, null);
}
};
const releaseClose = this.listenTo(this.ws, 'close', () => {
debugWs('Closed WS connection with', url);
errHandler();
});
const releaseError = this.listenTo(this.ws, 'error', error => {
debugWs('Closed WS connection with', url, 'error is', error);
errHandler();
});
const releaseMessage = this.listenTo(this.ws, 'message', message => {
debugWs('Received message', message.data);
const data = JSON.parse(message.data);
const id = data.id;
const request = this.requests[id] || this.callbacks[id];
if (!request) {
debugWs('Golos.onMessage error: unknown request ', id);
return;
}
delete this.requests[id];
this.onMessage(data, request);
});
this.releases = this.releases.concat([releaseOpen, releaseClose, releaseError, releaseMessage]);
});
this.startP = startP;
return startP;
}
stop() {
if (this.ws) {
try {
this.listenTo(this.ws, 'error', error => {});
this.ws.close();
} catch (err) {}
}
delete this.startP;
delete this.ws;
this.releases.forEach(release => release());
this.releases = [];
}
onMessage(message, request) {
const {
api,
data,
resolve,
reject,
start_time
} = request;
debugWs('-- Golos.onMessage -->', message.id);
const errorCause = message.error;
if (errorCause) {
const err = new Error(
// eslint-disable-next-line prefer-template
(errorCause.message || 'Failed to complete operation') + ' (see err.payload for the full error payload)');
err.payload = message;
reject(err);
return;
}
debugProtocol('Resolved', api, data, '->', message);
if (cbMethods.includes(data.method)) {
this.callbacks[message.id].cb(null, message.result);
} else {
delete this.requests[message.id];
resolve(message.result);
}
}
send(api, data, callback) {
const id = data.id || this.id++;
const startP = this.start();
this.currentP = startP.then(() => new Promise((resolve, reject) => {
if (!this.ws) {
reject(new Error('The WS connection was closed while this request was pending'));
return;
}
const payload = JSON.stringify({
id,
method: 'call',
jsonrpc: '2.0',
params: [api, data.method, data.params]
});
debugWs('Sending message', payload);
if (cbMethods.includes(data.method)) {
this.callbacks[id] = {
api,
data,
cb: callback
};
} else {
this.requests[id] = {
api,
data,
resolve,
reject,
start_time: Date.now()
};
}
this.ws.send(payload);
}));
this.currentP = (0, _promisify.nodeify)(this.currentP, callback);
return this.currentP;
}
}
exports.default = WsTransport;