@adt/json-rpc
Version:
Base JsonRpc classes. Represents all elements from JSON-RPC 2.0 specification http://www.jsonrpc.org/specification
633 lines (502 loc) • 16.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**
* 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;