custom-golos-js
Version:
Golos.js the JavaScript API for Golos blockchain
527 lines (435 loc) • 16 kB
JavaScript
;
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;