UNPKG

custom-golos-js

Version:

Golos.js the JavaScript API for Golos blockchain

527 lines (435 loc) 16 kB
'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = [];var _n = true;var _d = false;var _e = undefined;try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value);if (i && _arr.length === i) break; } } catch (err) { _d = true;_e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } }return _arr; }return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor); } }return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor; }; }(); var _events = require('events'); var _events2 = _interopRequireDefault(_events); var _bluebird = require('bluebird'); var _bluebird2 = _interopRequireDefault(_bluebird); var _cloneDeep = require('lodash/cloneDeep'); var _cloneDeep2 = _interopRequireDefault(_cloneDeep); var _defaults = require('lodash/defaults'); var _defaults2 = _interopRequireDefault(_defaults); var _detectNode = require('detect-node'); var _detectNode2 = _interopRequireDefault(_detectNode); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); var _config = require('../config'); var _config2 = _interopRequireDefault(_config); var _methods = require('./methods'); var _methods2 = _interopRequireDefault(_methods); var _utils = require('../utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); }return call && ((typeof call === "undefined" ? "undefined" : _typeof(call)) === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : _typeof(superClass))); }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var debugEmitters = (0, _debug2.default)('golos:emitters'); var debugProtocol = (0, _debug2.default)('golos:protocol'); var debugSetup = (0, _debug2.default)('golos:setup'); var debugWs = (0, _debug2.default)('golos:ws'); var WebSocket = void 0; if (_detectNode2.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'); } var DEFAULTS = { id: 0 }; var cbMethods = ['set_block_applied_callback', 'set_pending_transaction_callback', 'set_callback']; var expectedResponseMs = process.env.EXPECTED_RESPONSE_MS || 2000; var Golos = function (_EventEmitter) { _inherits(Golos, _EventEmitter); function Golos() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, Golos); var _this = _possibleConstructorReturn(this, (Golos.__proto__ || Object.getPrototypeOf(Golos)).call(this, options)); (0, _defaults2.default)(options, DEFAULTS); _this.options = (0, _cloneDeep2.default)(options); _this.id = 0; _this.inFlight = 0; _this.currentP = _bluebird2.default.fulfilled(); _this.isOpen = false; _this.releases = []; _this.requests = {}; _this.callbacks = {}; return _this; } _createClass(Golos, [{ key: 'setWebSocket', value: function setWebSocket(url) { console.warn("golos.api.setWebSocket(url) is now deprecated instead use golos.config.set('websocket',url)"); debugSetup('Setting WS', url); _config2.default.set('websocket', url); this.stop(); } }, { key: 'start', value: function start() { var _this2 = this; if (this.startP) { return this.startP; } var startP = new _bluebird2.default(function (resolve, reject) { if (startP !== _this2.startP) return; var url = _config2.default.get('websocket'); _this2.ws = new WebSocket(url); var releaseOpen = _this2.listenTo(_this2.ws, 'open', function () { debugWs('Opened WS connection with', url); _this2.isOpen = true; releaseOpen(); resolve(); }); var releaseClose = _this2.listenTo(_this2.ws, 'close', function () { debugWs('Closed WS connection with', url); _this2.isOpen = false; delete _this2.ws; _this2.stop(); if (startP.isPending()) { reject(new Error('The WS connection was closed before this operation was made')); } }); var releaseMessage = _this2.listenTo(_this2.ws, 'message', function (message) { debugWs('Received message', message.data); var data = JSON.parse(message.data); var id = data.id; var request = _this2.requests[id] || _this2.callbacks[id]; if (!request) { debugWs('Golos.onMessage error: unknown request ', id); return; } delete _this2.requests[id]; _this2.onMessage(data, request); }); _this2.releases = _this2.releases.concat([releaseOpen, releaseClose, releaseMessage]); }); this.startP = startP; return startP; } }, { key: 'stop', value: function stop() { debugSetup('Stopping...'); if (this.ws) this.ws.close(); delete this.startP; delete this.ws; this.releases.forEach(function (release) { return release(); }); this.releases = []; } }, { key: 'listenTo', value: function listenTo(target, eventName, callback) { debugEmitters('Adding listener for', eventName, 'from', target.constructor.name); if (target.addEventListener) target.addEventListener(eventName, callback);else target.on(eventName, callback); return function () { debugEmitters('Removing listener for', eventName, 'from', target.constructor.name); if (target.removeEventListener) target.removeEventListener(eventName, callback);else target.removeListener(eventName, callback); }; } }, { key: 'onMessage', value: function onMessage(message, request) { var api = request.api, data = request.data, resolve = request.resolve, reject = request.reject, start_time = request.start_time; debugWs('-- Golos.onMessage -->', message.id); var errorCause = message.error; if (errorCause) { var 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); } } }, { key: 'send', value: function send(api, data, callback) { var _this3 = this; debugSetup('Golos::send', api, data); var id = data.id || this.id++; var startP = this.start(); this.currentP = startP.then(function () { return new _bluebird2.default(function (resolve, reject) { if (!_this3.ws) { reject(new Error('The WS connection was closed while this request was pending')); return; } var payload = JSON.stringify({ id: id, method: 'call', jsonrpc: '2.0', params: [api, data.method, data.params] }); debugWs('Sending message', payload); if (cbMethods.includes(data.method)) { _this3.callbacks[id] = { api: api, data: data, cb: callback }; } else { _this3.requests[id] = { api: api, data: data, resolve: resolve, reject: reject, start_time: Date.now() }; } _this3.ws.send(payload); }); }).nodeify(callback); return this.currentP; } }, { key: 'streamBlockNumber', value: function streamBlockNumber() { var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'head'; var _this4 = this; var callback = arguments[1]; var ts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 200; if (typeof mode === 'function') { callback = mode; mode = 'head'; } var current = ''; var running = true; var update = function update() { if (!running) return; _this4.getDynamicGlobalPropertiesAsync().then(function (result) { var blockId = mode === 'irreversible' ? result.last_irreversible_block_num : result.head_block_number; if (blockId !== current) { if (current) { for (var i = current; i < blockId; i++) { if (i !== current) { callback(null, i); } current = i; } } else { current = blockId; callback(null, blockId); } } _bluebird2.default.delay(ts).then(function () { update(); }); }, function (err) { callback(err); }); }; update(); return function () { running = false; }; } }, { key: 'streamBlock', value: function streamBlock() { var _this5 = this; var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'head'; var callback = arguments[1]; if (typeof mode === 'function') { callback = mode; mode = 'head'; } var current = ''; var last = ''; var release = this.streamBlockNumber(mode, function (err, id) { if (err) { release(); callback(err); return; } current = id; if (current !== last) { last = current; _this5.getBlock(current, callback); } }); return release; } }, { key: 'streamTransactions', value: function streamTransactions() { var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'head'; var callback = arguments[1]; if (typeof mode === 'function') { callback = mode; mode = 'head'; } var release = this.streamBlock(mode, function (err, result) { if (err) { release(); callback(err); return; } if (result && result.transactions) { result.transactions.forEach(function (transaction) { callback(null, transaction); }); } }); return release; } }, { key: 'streamOperations', value: function streamOperations() { var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'head'; var callback = arguments[1]; if (typeof mode === 'function') { callback = mode; mode = 'head'; } var release = this.streamTransactions(mode, function (err, transaction) { if (err) { release(); callback(err); return; } transaction.operations.forEach(function (operation) { callback(null, operation); }); }); return release; } }]); return Golos; }(_events2.default); // Generate Methods from methods.js _methods2.default.forEach(function (method) { var methodName = method.method_name || (0, _utils.camelCase)(method.method); var methodParams = method.params || []; var defaultParms = {}; var hasDefaultValues = method.has_default_values; if (hasDefaultValues) { methodParams.forEach(function (param) { var _param$split = param.split('='), _param$split2 = _slicedToArray(_param$split, 2), p = _param$split2[0], value = _param$split2[1]; defaultParms[p] = value ? JSON.parse(value) : ''; }); } Golos.prototype[methodName + 'With'] = function Golos$$specializedSendWith(options, callback) { var params = methodParams.map(function (param) { return options[hasDefaultValues ? param.split('=')[0] : param]; }); return this.send(method.api, { method: method.method, params: params }, callback); }; Golos.prototype[methodName] = function Golos$specializedSend() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var options = {}; if (hasDefaultValues) { var argsWithoutCb = args.slice(0, args.length - 1); methodParams.forEach(function (param, i) { var _param$split3 = param.split('='), _param$split4 = _slicedToArray(_param$split3, 2), p = _param$split4[0], value = _param$split4[1]; if (argsWithoutCb[i]) { options[p] = argsWithoutCb[i]; } }); options = Object.assign({}, defaultParms, options); } else { var opt = methodParams.reduce(function (memo, param, i) { memo[param] = args[i]; return memo; }, {}); options = Object.assign({}, opt); } var callback = args[hasDefaultValues ? args.length - 1 : methodParams.length]; return this[methodName + 'With'](options, callback); }; }); _bluebird2.default.promisifyAll(Golos.prototype); Golos.prototype['setBlockAppliedCallback'] = function Golos$setCallback(type, callback) { return this.send('database_api', { method: 'set_block_applied_callback', params: [type] }, callback); }; Golos.prototype['setPendingTransactionCallback'] = function Golos$setCallback(callback) { return this.send('database_api', { method: 'set_pending_transaction_callback', params: [] }, callback); }; Golos.prototype['setPrivateMessageCallback'] = function Golos$setCallback(query, callback) { return this.send('private_message', { method: 'set_callback', params: [query] }, callback); }; // Export singleton instance var golos = new Golos(); exports = module.exports = golos; exports.Golos = Golos; exports.Golos.DEFAULTS = DEFAULTS;