UNPKG

@adt/json-rpc

Version:

Base JsonRpc classes. Represents all elements from JSON-RPC 2.0 specification http://www.jsonrpc.org/specification

645 lines (513 loc) 16.5 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (function() { var current = global.JsonRpc; var exports = (global.JsonRpc = {}); factory(exports); global.JsonRpc = exports; exports.noConflict = function() { global.JsonRpc = current; return exports; }; })(); }(this, (function (exports) { 'use strict'; /** * JSON-RPC Element * Base class for all JSON-RPC messages * This is abstract class, do not try to create instance of this class! * @class * @abstract */ function JsonRpcElement() { throw new Error("JsonRpcElement is an abstract class. Can't instantiate or run"); } JsonRpcElement.prototype = Object.freeze(Object.create(null, /** @lends JsonRpcElement.prototype */{ jsonrpc: { value: "2.0" }, serialize: { value: function value() { return JSON.stringify(this); } } })); 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 asyncGenerator = function () { function AwaitValue(value) { this.value = value; } function AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; if (value instanceof AwaitValue) { Promise.resolve(value.value).then(function (arg) { resume("next", arg); }, function (arg) { resume("throw", arg); }); } else { settle(result.done ? "return" : "normal", result.value); } } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } } if (typeof Symbol === "function" && Symbol.asyncIterator) { AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; } AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); }; AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); }; return { wrap: function (fn) { return function () { return new AsyncGenerator(fn.apply(this, arguments)); }; }, await: function (value) { return new AwaitValue(value); } }; }(); var validateId = function validateId(o) { if ("id" in o === false) { throw new Error("Required 'id' param is not present"); } }; var validateMethod = function validateMethod(method) { var methodType = typeof method === "undefined" ? "undefined" : _typeof(method); if (methodType !== "string") { throw new Error("Method must be a string, but '" + methodType + "' given"); } }; var validateParams = function validateParams(params) { var paramsType = typeof params === "undefined" ? "undefined" : _typeof(params); var paramsIsArray = Array.isArray(params); if (paramsType === "undefined") { return; } if (paramsIsArray === false && paramsType !== "object" && paramsType !== "undefined") { throw new Error("Params must be an array, object or undefined, but '" + paramsType + "' given"); } }; var validateArguments = function validateArguments(args) { if (typeof args === "undefined") { throw new Error("Required parameters object not present"); } }; /** * JSON-RPC Error object * @class * @param {Object} o Parameters object * @param {Number} o.code Error code * @param {String} [o.message=""] Error message * @param {?Object} [o.data=undefined] Additional data associated with error */ function JsonRpcError(o) { if (this instanceof JsonRpcError === false) { return new JsonRpcError(o); } validateArguments(o); if (typeof o.code !== "number") { throw "Error code must be integer"; } this.code = o.code; this.message = "message" in o ? o.message : ""; this.data = o.data; return Object.freeze(this); } JsonRpcError.prototype = Object.create(null, /** @lends JsonRpcError.prototype */{ code: { value: 0, writable: true }, message: { value: "", writable: true }, data: { value: undefined, writable: true } }); /** * Standard JSON-RPC errors * @type {Object} */ JsonRpcError.ERRORS = Object.freeze(Object.create(null, { PARSE_ERROR: { value: Object.freeze(Object.create(JsonRpcError.prototype, { code: { value: -32700, enumerable: true }, message: { value: "Parse error", enumerable: true } })), enumerable: true }, INVALID_REQUEST: { value: Object.freeze(Object.create(JsonRpcError.prototype, { code: { value: -32600, enumerable: true }, message: { value: "Invalid Request", enumerable: true } })), enumerable: true }, METHOD_NOT_FOUND: { value: Object.freeze(Object.create(JsonRpcError.prototype, { code: { value: -32601, enumerable: true }, message: { value: "Method not found", enumerable: true } })), enumerable: true }, INVALID_PARAMS: { value: Object.freeze(Object.create(JsonRpcError.prototype, { code: { value: -32602, enumerable: true }, message: { value: "Invalid params", enumerable: true } })), enumerable: true }, INTERNAL_ERROR: { value: Object.freeze(Object.create(JsonRpcError.prototype, { code: { value: -32603, enumerable: true }, message: { value: "Internal error", enumerable: true } })), enumerable: true } })); // TODO: For now this is only one level shallow copy, but should be real deep copy with omitting functions var copyParams = function copyParams(params) { if (typeof params === "undefined") { return undefined; } else if (Array.isArray(params)) { return Object.freeze(params.splice(0)); } else { var ret = Object.create(null); for (var a in params) { ret[a] = params[a]; Object.defineProperty(ret, a, { value: params[a] }); } return Object.freeze(ret); } }; /** * JSON-RPC Event * TODO: Maybe length of 'method' parameter should be checked? Specification does not say anything about empty string as method name * @class * @param {Object} o Parameters object * @param {String} o.method Method name * @param {?Array} [o.params=undefined] Event params */ function JsonRpcEvent(o) { validateArguments(o); validateMethod(o.method); var method = o.method; validateParams(o.params); var params = copyParams(o.params); var instance = Object.create(JsonRpcEvent.prototype); instance.method = method; instance.params = params; return Object.freeze(instance); } JsonRpcEvent.prototype = Object.create(JsonRpcElement.prototype, /** @lends JsonRpcElement.prototype */{ method: { writable: true }, params: { writable: true }, serialize: { value: function value() { return JSON.stringify({ jsonrpc: this.jsonrpc, method: this.method, params: this.params }); } }, toJSON: { value: function value() { return this.serialize(); } } }); /** * UUID * @constructor */ function UUID(uuid) { return Object.create(UUID.prototype, /** @lends UUID.prototype */{ value: { value: uuid, writable: true }, serialize: { value: function value() { var val = this.value; return [val[0] + val[1], val[2], val[3], val[4], val[5] + val[6] + val[7]].join("-"); } } }); } UUID.randomUUID = function () { return UUID(guid()); }; UUID.fromString = function (str) { var replaced = str.replace(/-/g, ""); var a = []; for (var i = 0; i < 32; i += 4) { a.push(replaced.slice(i, i + 4)); } return UUID(a); }; /** * Helper function * @returns {String} */ function s4() { return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); } /** * Helper function * @return {Array} */ function guid() { var ar = []; for (var i = 0; i < 8; i++) { ar.push(s4()); } return ar; } /** * JSON-RPC Request * TODO: Maybe length of 'method' parameter should be checked? Specification does not say anything about empty string as method name * @class * @param {Object} o Parameters object * @param {String} o.method * @param {Array|Object|undefined} o.params Params must be an Array, Object or undefined * @param {?String} [o.id=UUID] Request id if empty it will be set to randomly generated UUID TODO: For now id is set always to 1!!! */ function JsonRpcRequest(o) { validateArguments(o); validateMethod(o.method); var method = o.method; validateParams(o.params); var params = copyParams(o.params); var id = "id" in o ? o.id : UUID.randomUUID().serialize(); //return Object.freeze(Object.create(JsonRpcRequest.prototype,)); var instance = Object.create(JsonRpcRequest.prototype); instance.id = id; instance.method = method; instance.params = params; return Object.freeze(instance); } JsonRpcRequest.prototype = Object.create(JsonRpcElement.prototype, /** @lends JsonRpcRequest.prototype */{ method: { writable: true }, params: { writable: true }, id: { writable: true }, /** * Serialize request object to string * @return {String} */ serialize: { value: function value() { var ret = { jsonrpc: this.jsonrpc, id: this.id, method: this.method }; if (typeof this.params !== "undefined") { ret.params = this.params; } return JSON.stringify(ret); } }, toJSON: { value: function value() { return this.serialize(); } } }); /** * JSON-RPC Response * This is abstract class, do not try to create instance of this class! * Base class for JSON-RPC responses * @class * @abstract */ function JsonRpcResponse() { throw new Error("JsonRpcResponse is an abstract class. Can't instantiate or run"); } JsonRpcResponse.prototype = Object.freeze(Object.create(JsonRpcElement.prototype, { id: { value: null, writable: true } })); /** * JSON-RPC Error Response * @class * @param {Object} o Parameters object * @param {String} o.id * @param {JsonRpcError} o.error */ function JsonRpcResponseError(o) { validateArguments(o); validateId(o); if (!(o.error instanceof JsonRpcError)) { throw Error("error property is not an instance of JsonRpcError!"); } var id = o.id; var error = o.error; return Object.freeze(Object.create(JsonRpcResponseError.prototype, { id: { value: id }, error: { value: error }, /** * Serialize request object to string * @return {String} */ serialize: { value: function value() { return JSON.stringify({ jsonrpc: this.jsonrpc, id: this.id, error: this.error }); } }, toJSON: { value: function value() { return this.serialize(); } } })); } JsonRpcResponseError.prototype = Object.freeze(Object.create(JsonRpcResponse.prototype)); /** * JSON-RPC Response result * @class * @param {Object} o Parameters object * @param {String} o.id * @param {Object} o.result */ function JsonRpcResponseResult(o) { validateArguments(o); validateId(o); if ("result" in o === false) { throw new Error("Required 'result' param is not present"); } var id = o.id; var result = o.result; return Object.freeze(Object.create(JsonRpcResponseResult.prototype, /** @lends JsonRpcResponseResult.prototype */{ id: { value: id }, result: { value: result }, /** * Serialize request object to string * @return {String} */ serialize: { value: function value() { return JSON.stringify({ jsonrpc: this.jsonrpc, id: this.id, result: this.result }); } }, toJSON: { value: function value() { return this.serialize(); } } })); } JsonRpcResponseResult.prototype = Object.freeze(Object.create(JsonRpcResponse.prototype)); //import JsonRpcElement from "./JsonRpcElement"; /** * Parses Json to JsonRpc object * @param {String} str String to parse * @param {Boolean} [weak=false] Don't check for existence of "jsonrpc" property in object * @return JsonRpcElement */ var parse = function parse(str, weak) { var u = "undefined"; var obj = void 0; try { obj = JSON.parse(str); } catch (e) { throw new Error("String " + str + " is not a valid JSON"); } if (weak === true && "jsonrpc" in obj === false) { // TODO: Dedicated exception throw new Error("Missing 'jsonrpc' property in unserialized. You can omit this check by using the `weak` param"); } var _obj = obj, id = _obj.id, method = _obj.method, result = _obj.result, error = _obj.error, params = _obj.params; if ((typeof id === "undefined" ? "undefined" : _typeof(id)) !== u) { // Request or response if ((typeof method === "undefined" ? "undefined" : _typeof(method)) !== u) { if ((typeof params === "undefined" ? "undefined" : _typeof(params)) === u) { throw new Error("Missing 'method' property in unserialized object"); } return JsonRpcRequest({ id: id, method: method, params: params }); } else if ((typeof result === "undefined" ? "undefined" : _typeof(result)) !== u) { return JsonRpcResponseResult({ id: id, result: result }); } else if ((typeof error === "undefined" ? "undefined" : _typeof(error)) !== u) { var code = error.code, message = error.message, data = error.data; return JsonRpcResponseError({ id: id, error: JsonRpcError({ code: code, message: message, data: data }) }); } else { // TODO: Dedicated Exception throw new Error("Unserialized object is not a valid JSON-RPC element"); } } else { // Event return JsonRpcEvent({ method: method, params: params }); } }; JsonRpcElement.parse = parse; JsonRpcElement.fromJSON = parse; exports.JsonRpcElement = JsonRpcElement; exports.JsonRpcError = JsonRpcError; exports.JsonRpcEvent = JsonRpcEvent; exports.JsonRpcRequest = JsonRpcRequest; exports.JsonRpcResponse = JsonRpcResponse; exports.JsonRpcResponseError = JsonRpcResponseError; exports.JsonRpcResponseResult = JsonRpcResponseResult; Object.defineProperty(exports, '__esModule', { value: true }); })));