jsonbird
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -7,19 +7,55 @@ { | ||
"globals": { | ||
"console": true, | ||
"dump": true, | ||
"__dirname": true, | ||
"clearImmediate": true, | ||
"clearInterval": true, | ||
"clearTimeout": true, | ||
"setImmediate": true, | ||
"setInterval": true, | ||
"setTimeout": true, | ||
"Buffer": true | ||
"__dirname": false, | ||
"Buffer": false, | ||
"clearImmediate": false, | ||
"clearInterval": false, | ||
"clearTimeout": false, | ||
"console": false, | ||
"setImmediate": false, | ||
"setInterval": false, | ||
"setTimeout": false | ||
}, | ||
"parser": "babel-eslint", | ||
"parserOptions": { | ||
"ecmaVersion": 7, | ||
"sourceType": "module" | ||
"ecmaVersion": 2017, | ||
"sourceType": "script" | ||
}, | ||
"plugins": [ | ||
"import" | ||
], | ||
"rules": { | ||
"array-bracket-spacing": [ | ||
"error", | ||
"never" | ||
], | ||
"array-callback-return": "error", | ||
"arrow-parens": "off", | ||
"arrow-spacing": "error", | ||
"brace-style": [ | ||
"error", | ||
"stroustrup", | ||
{ | ||
"allowSingleLine": true | ||
} | ||
], | ||
"camelcase": [ | ||
"error", | ||
{ | ||
"properties": "always" | ||
} | ||
], | ||
"comma-dangle": [ | ||
"error", | ||
"always-multiline" | ||
], | ||
"comma-spacing": [ | ||
"error", | ||
{ | ||
"after": true | ||
} | ||
], | ||
"comma-style": [ | ||
"error", | ||
"last" | ||
], | ||
"complexity": "warn", | ||
@@ -33,4 +69,71 @@ "consistent-return": "error", | ||
], | ||
"dot-notation": [ | ||
"error", | ||
{ | ||
"allowPattern": "^[a-z]+(_[a-z]+)+$" | ||
} | ||
], | ||
"eol-last": "error", | ||
"eqeqeq": "error", | ||
"generator-star-spacing": [ | ||
"error", | ||
{ | ||
"after": true, | ||
"before": false | ||
} | ||
], | ||
"global-require": "error", | ||
"import/default": "error", | ||
"import/export": "error", | ||
"import/first": "error", | ||
"import/named": "error", | ||
"import/namespace": "error", | ||
"import/newline-after-import": "error", | ||
"import/no-absolute-path": "error", | ||
"import/no-extraneous-dependencies": [ | ||
"error", { | ||
"devDependencies": [ | ||
"test/**", | ||
"webpack.config.js" | ||
] | ||
} | ||
], | ||
"import/no-mutable-exports": "error", | ||
"import/no-named-default": "error", | ||
"import/no-unassigned-import": "error", | ||
"import/no-webpack-loader-syntax": "error", | ||
"import/order": [ | ||
"error", | ||
{ | ||
"groups": [ | ||
["builtin", "external"], | ||
["index","sibling","parent","internal"] | ||
], | ||
"newlines-between": "always" | ||
} | ||
], | ||
"indent": [ | ||
"error", | ||
4, | ||
{ | ||
"SwitchCase": 1 | ||
} | ||
], | ||
"key-spacing": [ | ||
"error", | ||
{ | ||
"afterColon": true, | ||
"beforeColon": false, | ||
"mode": "minimum" | ||
} | ||
], | ||
"keyword-spacing": "error", | ||
"linebreak-style": [ | ||
"error", | ||
"unix" | ||
], | ||
"max-len": [ | ||
"error", | ||
140 | ||
], | ||
"max-nested-callbacks": [ | ||
@@ -44,2 +147,18 @@ "error", | ||
], | ||
"new-cap": [ | ||
"error", | ||
{ | ||
"capIsNewExceptions": [ | ||
"Array", | ||
"Boolean", | ||
"Error", | ||
"Number", | ||
"PageMod", | ||
"Object", | ||
"QueryInterface", | ||
"String", | ||
"Symbol" | ||
] | ||
} | ||
], | ||
"no-array-constructor": "error", | ||
@@ -54,3 +173,10 @@ "no-caller": "error", | ||
], | ||
"no-confusing-arrow": [ | ||
"error", | ||
{ | ||
"allowParens": true | ||
} | ||
], | ||
"no-const-assign": "error", | ||
"no-div-regex": "error", | ||
"no-dupe-args": "error", | ||
@@ -60,2 +186,3 @@ "no-dupe-class-members": "error", | ||
"no-duplicate-case": "error", | ||
"no-else-return": "error", | ||
"no-empty-character-class": "error", | ||
@@ -68,4 +195,12 @@ "no-empty-pattern": "error", | ||
"no-func-assign": "error", | ||
"no-implicit-coercion": "error", | ||
"no-implicit-coercion": [ | ||
"error", | ||
{ | ||
"boolean": true, | ||
"number": true, | ||
"string": true | ||
} | ||
], | ||
"no-implied-eval": "error", | ||
"no-inner-declarations": "error", | ||
"no-invalid-regexp": "error", | ||
@@ -75,2 +210,3 @@ "no-iterator": "error", | ||
"no-loop-func": "error", | ||
"no-multi-str": "error", | ||
"no-native-reassign": "error", | ||
@@ -87,2 +223,3 @@ "no-negated-in-lhs": "error", | ||
"no-proto": "error", | ||
"no-prototype-builtins": "error", | ||
"no-regex-spaces": "error", | ||
@@ -92,5 +229,8 @@ "no-return-assign": "error", | ||
"no-self-compare": "error", | ||
"no-spaced-func": "error", | ||
"no-sparse-arrays": "error", | ||
"no-tabs": "error", | ||
"no-this-before-super": "error", | ||
"no-throw-literal": "error", | ||
"no-trailing-spaces": "error", | ||
"no-undef": "error", | ||
@@ -100,2 +240,4 @@ "no-unexpected-multiline": "error", | ||
"no-unreachable": "error", | ||
"no-unsafe-finally": "error", | ||
"no-unsafe-negation": "error", | ||
"no-unused-vars": [ | ||
@@ -108,5 +250,10 @@ "error", | ||
"no-useless-call": "error", | ||
"no-useless-computed-key": "error", | ||
"no-var": "error", | ||
"no-void": "error", | ||
"no-with": "error", | ||
"object-curly-spacing": [ | ||
"error", | ||
"never" | ||
], | ||
"one-var": [ | ||
@@ -116,10 +263,87 @@ "error", | ||
], | ||
"prefer-arrow-callback": "error", | ||
"prefer-const": "error", | ||
"prefer-rest-params": "error", | ||
"prefer-spread": "error", | ||
"quote-props": [ | ||
"error", | ||
"as-needed" | ||
], | ||
"quotes": [ | ||
"error", | ||
"single", | ||
{ | ||
"allowTemplateLiterals": true | ||
} | ||
], | ||
"radix": "error", | ||
"rest-spread-spacing": "error", | ||
"semi": [ | ||
"error", | ||
"always" | ||
], | ||
"semi-spacing": [ | ||
"error", | ||
{ | ||
"after": true, | ||
"before": false | ||
} | ||
], | ||
"space-before-blocks": [ | ||
"error", | ||
"always" | ||
], | ||
"space-before-function-paren": [ | ||
"error", | ||
{ | ||
"anonymous": "ignore", | ||
"named": "never" | ||
} | ||
], | ||
"space-in-parens": [ | ||
"error", | ||
"never" | ||
], | ||
"space-infix-ops": "error", | ||
"space-unary-ops": [ | ||
"error", | ||
{ | ||
"nonwords": false, | ||
"words": false | ||
} | ||
], | ||
"spaced-comment": [ | ||
"error", | ||
"always" | ||
], | ||
"strict": "error", | ||
"unicode-bom": "error", | ||
"use-isnan": "error", | ||
"valid-typeof": "error" | ||
"valid-jsdoc": [ | ||
"error", | ||
{ | ||
"prefer": { | ||
"arg": "param", | ||
"argument": "param", | ||
"class": "constructor", | ||
"returns": "return", | ||
"virtual": "abstract" | ||
}, | ||
"requireParamDescription": false, | ||
"requireReturn": false, | ||
"requireReturnDescription": false | ||
} | ||
], | ||
"valid-typeof": [ | ||
"error", | ||
{ | ||
"requireStringLiterals": true | ||
} | ||
], | ||
"wrap-iife": "error", | ||
"yoda": [ | ||
"error", | ||
"never" | ||
] | ||
} | ||
} |
@@ -10,3 +10,3 @@ 'use strict'; | ||
while(1) { | ||
while (1) { | ||
const parent = Object.getPrototypeOf(proto); | ||
@@ -13,0 +13,0 @@ |
@@ -6,2 +6,3 @@ 'use strict'; | ||
const JSONParser = require('jsonparse'); | ||
const RPCRequestError = require('./RPCRequestError'); | ||
@@ -13,2 +14,3 @@ const RPCResponseError = require('./RPCResponseError'); | ||
const NOOP = () => {}; | ||
const RETURN_TRUE = () => true; | ||
const PRIVATE = Symbol('JSONBird PRIVATE'); | ||
@@ -27,2 +29,9 @@ const REQUEST_ERROR_ID = Symbol('JSONBird RPCRequestError id'); | ||
finishOnEnd: true, | ||
pingReceive: true, | ||
pingMethod: 'jsonbird.ping', | ||
pingInterval: 2000, | ||
pingTimeout: 1000, | ||
pingNow: Date.now.bind(Date), | ||
setTimeout: setTimeout, | ||
clearTimeout: clearTimeout, | ||
}; | ||
@@ -39,2 +48,36 @@ | ||
/** | ||
* This event is fired if an uncaught error occurred | ||
* | ||
* Most errors end up at the caller of our functions or at the remote peer, instead of this event. | ||
* Note that if you do not listen for this event on node.js, your process might exit. | ||
* | ||
* @event JSONBird#error | ||
* @param {Error} error | ||
*/ | ||
/** | ||
* This event is fired if our peer sent us something that we were unable to parse. | ||
* | ||
* These kind of errors do not end up at the 'error' event | ||
* | ||
* @event JSONBird#protocolError | ||
* @param {Error} error | ||
*/ | ||
/** | ||
* The most recent ping sent to our peer succeeded | ||
* | ||
* @event JSONBird#pingSuccess | ||
* @param {number} delay How long the ping took to resolve (in milliseconds) | ||
*/ | ||
/** | ||
* The most recent ping sent to our peer timed out or resulted in an error | ||
* | ||
* @event JSONBird#pingFail | ||
* @param {number} consecutiveFails The amount of consecutive pings that failed | ||
* @param {Error} error | ||
*/ | ||
/** | ||
* Converts any javascript `Error` object to a JSON-RPC error object | ||
@@ -45,3 +88,3 @@ * | ||
* object containing the `fileName`, `lineNumber`, `columnNumber` and `stack` of the `error` | ||
* @returns {{code: number, message: string, data: *}} | ||
* @return {{code: number, message: string, data: *}} | ||
*/ | ||
@@ -77,3 +120,3 @@ static errorToResponseObject(error, includeErrorStack) { | ||
* @param {*} jsonrpc | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -88,3 +131,3 @@ static isValidVersion(jsonrpc) { | ||
* @param {*} id | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -99,3 +142,3 @@ static isValidID(id) { | ||
* @param {*} method | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -110,3 +153,3 @@ static isValidMethodName(method) { | ||
* @param {*} params | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -124,3 +167,3 @@ static isValidParams(params) { | ||
* @param {string} name | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -156,2 +199,9 @@ static isObjectBuiltinFunction(object, name) { | ||
* @param {boolean} [optionsArg.finishOnEnd=true] | ||
* @param {boolean} [optionsArg.pingReceive=true] | ||
* @param {string} [optionsArg.pingMethod='jsonbird.ping'] | ||
* @param {number} [optionsArg.pingInterval=2000] | ||
* @param {number} [optionsArg.pingTimeout=1000] | ||
* @param {number} [optionsArg.pingNow=Date.now] Timer function used to figure out ping delays | ||
* @param {Function} [optionsArg.setTimeout=global.setTimeout] | ||
* @param {Function} [optionsArg.clearTimeout=global.clearTimeout] | ||
*/ | ||
@@ -210,2 +260,12 @@ constructor(optionsArg = {}) { | ||
writableMode: options.writableMode, | ||
pingReceive: Boolean(options.pingReceive), | ||
pingMethod: String(options.pingMethod), | ||
pingInterval: 0, | ||
pingTimeout: 0, | ||
pingNow: options.pingNow, | ||
isSendingPings: false, | ||
pingTimer: 0, | ||
pingConsecutiveFails: 0, | ||
setTimeout: options.setTimeout, | ||
clearTimeout: options.clearTimeout, | ||
}); | ||
@@ -218,2 +278,4 @@ | ||
this.finishOnEnd = options.finishOnEnd; | ||
this.pingInterval = options.pingInterval; | ||
this.pingTimeout = options.pingTimeout; | ||
@@ -234,2 +296,3 @@ if (this.writableMode === 'json-stream') { | ||
this[PRIVATE].finished = true; | ||
this.stopPinging(); | ||
@@ -247,4 +310,5 @@ if (this.endOnFinish && !this[PRIVATE].ended) { | ||
this[PRIVATE].ended = true; | ||
this.stopPinging(); | ||
if (this.finishOnEnd && !this[PRIVATE].finished) { | ||
if (this.finishOnEnd && !this.finished) { | ||
this.waitForPendingRequests().then(() => { | ||
@@ -255,2 +319,6 @@ this.end(); // finish | ||
}); | ||
if (this.pingReceive) { | ||
this.method(this.pingMethod, RETURN_TRUE); | ||
} | ||
} | ||
@@ -261,3 +329,3 @@ | ||
* | ||
* @returns {string|number} | ||
* @return {string|number} | ||
*/ | ||
@@ -279,2 +347,44 @@ generateId() { | ||
/** | ||
* The HTML setTimeout function | ||
* | ||
* This function may be overridden for unit tests | ||
* @return {Function} https://html.spec.whatwg.org/#dom-settimeout | ||
*/ | ||
get setTimeout() { | ||
return this[PRIVATE].setTimeout; | ||
} | ||
/** | ||
* The HTML clearTimeout function | ||
* | ||
* This function may be overridden for unit tests | ||
* @return {Function} https://html.spec.whatwg.org/#dom-cleartimeout | ||
*/ | ||
get clearTimeout() { | ||
return this[PRIVATE].clearTimeout; | ||
} | ||
/** | ||
* Has the readable side of this duplex stream been ended? | ||
* | ||
* (has the 'end' event been emitted) | ||
* | ||
* @return {boolean} | ||
*/ | ||
get ended() { | ||
return this[PRIVATE].ended; | ||
} | ||
/** | ||
* Has the writable side of this duplex stream been finished? | ||
* | ||
* (has the 'finish' event been emitted) | ||
* | ||
* @return {boolean} | ||
*/ | ||
get finished() { | ||
return this[PRIVATE].finished; | ||
} | ||
/** | ||
* This is a string that will be appended to the id of all request objects that we send out. | ||
@@ -285,3 +395,3 @@ * | ||
* | ||
* @returns {string} | ||
* @return {string} | ||
*/ | ||
@@ -320,3 +430,3 @@ get sessionId() { | ||
* | ||
* @returns {string} "object", "json-stream" or "json-message" | ||
* @return {string} "object", "json-stream" or "json-message" | ||
*/ | ||
@@ -356,3 +466,3 @@ get writableMode() { | ||
* | ||
* @returns {string} "object" or "json-stream" | ||
* @return {string} "object" or "json-stream" | ||
*/ | ||
@@ -368,3 +478,3 @@ get readableMode() { | ||
* | ||
* @returns {string} | ||
* @return {string} | ||
*/ | ||
@@ -379,3 +489,3 @@ get endOfJSONWhitespace() { | ||
* | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -400,3 +510,3 @@ get endOnFinish() { | ||
* | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -420,3 +530,3 @@ get finishOnEnd() { | ||
* | ||
* @returns {number} | ||
* @return {number} | ||
*/ | ||
@@ -430,3 +540,3 @@ get serverPending() { | ||
* | ||
* @returns {number} | ||
* @return {number} | ||
*/ | ||
@@ -444,3 +554,3 @@ get clientPending() { | ||
* | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -466,3 +576,3 @@ get receiveErrorStack() { | ||
* | ||
* @returns {boolean} | ||
* @return {boolean} | ||
*/ | ||
@@ -486,3 +596,3 @@ get sendErrorStack() { | ||
* | ||
* @returns {number} | ||
* @return {number} | ||
*/ | ||
@@ -504,2 +614,64 @@ get defaultTimeout() { | ||
/** | ||
* If `true` a method with the name `this.pingMethod` is added which simply returns true as fast as possible. | ||
* @return {boolean} | ||
*/ | ||
get pingReceive() { | ||
return this[PRIVATE].pingReceive; | ||
} | ||
/** | ||
* Are we currently sending pings to our peer? | ||
* | ||
* In other words, has `this.startPinging()` been called? | ||
* @return {boolean} | ||
*/ | ||
get isSendingPings() { | ||
return this[PRIVATE].isSendingPings; | ||
} | ||
/** | ||
* The method name used when receiving or sending pings. | ||
* @return {string} | ||
*/ | ||
get pingMethod() { | ||
return this[PRIVATE].pingMethod; | ||
} | ||
/** | ||
* The time (in milliseconds) between each ping if `isSendingPings` is true. | ||
* This time is in addition to the time spent waiting for the previous ping to settle. | ||
* | ||
* @return {number} milliseconds | ||
*/ | ||
get pingInterval() { | ||
return this[PRIVATE].pingInterval; | ||
} | ||
/** | ||
* The time (in milliseconds) between each ping if `isSendingPings` is true. | ||
* This time is in addition to the time spent waiting for the previous ping to settle. | ||
* | ||
* @param {number} value milliseconds | ||
*/ | ||
set pingInterval(value) { | ||
this[PRIVATE].pingInterval = value; | ||
} | ||
/** | ||
* The maximum amount of time (in milliseconds) to wait for a ping method call to resolve. | ||
* @return {number} milliseconds | ||
*/ | ||
get pingTimeout() { | ||
return this[PRIVATE].pingTimeout; | ||
} | ||
/** | ||
* The maximum amount of time (in milliseconds) to wait for a ping method call to resolve. | ||
* @param {number} value milliseconds | ||
*/ | ||
set pingTimeout(value) { | ||
this[PRIVATE].pingTimeout = value; | ||
} | ||
/** | ||
* Returns a promise which resolves as soon as all pending requests (as a server) have had their appropriate responses sent to the | ||
@@ -510,3 +682,3 @@ * underlying readable stream. | ||
* | ||
* @returns {Promise} | ||
* @return {Promise} | ||
*/ | ||
@@ -523,3 +695,3 @@ waitForPendingResponses() { | ||
* | ||
* @returns {Promise} | ||
* @return {Promise} | ||
*/ | ||
@@ -687,3 +859,3 @@ waitForPendingRequests() { | ||
* @param {...*} args | ||
* @returns {Promise} A promise resolving with the return value of the method, or rejecting with an error | ||
* @return {Promise} A promise resolving with the return value of the method, or rejecting with an error | ||
*/ | ||
@@ -739,3 +911,3 @@ callLocal(name, ...args) { | ||
* @param {...*} args | ||
* @returns {*} The return value of the method | ||
* @return {*} The return value of the method | ||
*/ | ||
@@ -785,3 +957,3 @@ callLocalAndSendResponse(id, name, ...args) { | ||
* | ||
* @returns {Promise} A Promise which will resole with the return value of the remote method | ||
* @return {Promise} A Promise which will resole with the return value of the remote method | ||
*/ | ||
@@ -805,3 +977,3 @@ call(nameOrOptions, ...args) { | ||
if (this[PRIVATE].finished) { | ||
if (this.finished) { | ||
// note: notify() does have this check because it does not care about response objects | ||
@@ -823,4 +995,7 @@ return Promise.reject(new Error( | ||
const responsePromise = new Promise((resolve, reject) => { | ||
const timer = timeout && setTimeout( | ||
() => reject(new RPCRequestError(Error(`Remote Call timed out after ${timeout}ms`), -32000)), | ||
const timer = timeout && this.setTimeout( | ||
() => { | ||
pendingData.timer = 0; | ||
reject(new RPCRequestError(Error(`Remote Call timed out after ${timeout}ms`), -32000)); | ||
}, | ||
timeout | ||
@@ -844,3 +1019,3 @@ ); | ||
if (pendingData.timer) { | ||
clearTimeout(pendingData.timer); | ||
this.clearTimeout(pendingData.timer); | ||
} | ||
@@ -871,3 +1046,3 @@ }); | ||
* after this time. | ||
* @returns {Function} | ||
* @return {Function} | ||
*/ | ||
@@ -889,3 +1064,3 @@ bindCall(nameOrOptions) { | ||
* @param {...*} args | ||
* @returns {Promise} A promise resolving with `undefined` and only rejects when an internal JSONBird Error occurs. | ||
* @return {Promise} A promise resolving with `undefined` and only rejects when an internal JSONBird Error occurs. | ||
*/ | ||
@@ -947,3 +1122,3 @@ notifyLocal(name, ...args) { | ||
* | ||
* @returns {Promise} | ||
* @return {Promise} | ||
*/ | ||
@@ -983,3 +1158,3 @@ notify(nameOrOptions, ...args) { | ||
* after this time. | ||
* @returns {Function} | ||
* @return {Function} | ||
*/ | ||
@@ -1040,3 +1215,3 @@ bindNotify(nameOrOptions) { | ||
} | ||
catch(err) { | ||
catch (err) { | ||
// We can not know for certain that this was a request object because JSONBird might be used bidirectionally. | ||
@@ -1069,3 +1244,3 @@ // So assume this is a request object, not sending a response at all is worse than sending too much. | ||
} | ||
catch(err) { | ||
catch (err) { | ||
// We can not know for certain that this was a request object because JSONBird might be used bidirectionally. | ||
@@ -1109,3 +1284,3 @@ // So assume this is a request object, not sending a response at all is worse than sending too much. | ||
* @param {Object} object plain old javascript options | ||
* @returns {Promise} Resolves with `undefined` or it rejects with a `RPCRequestError` or `RPCResponseError` if the object is malformed | ||
* @return {Promise} Resolves with `undefined` or it rejects with a `RPCRequestError` or `RPCResponseError` if the object is malformed | ||
*/ | ||
@@ -1138,3 +1313,3 @@ handleObject(object) { | ||
* @param {Object} object plain old javascript options | ||
* @returns {Promise} Resolves with `undefined` or it rejects with a `RPCRequestError` if the object is malformed | ||
* @return {Promise} Resolves with `undefined` or it rejects with a `RPCRequestError` if the object is malformed | ||
*/ | ||
@@ -1202,3 +1377,3 @@ handleRequestObject(object) { | ||
* @param {Object} object plain old javascript options | ||
* @returns {Promise} Resolves with `undefined` or it rejects with a `RPCResponseError` if the object is malformed | ||
* @return {Promise} Resolves with `undefined` or it rejects with a `RPCResponseError` if the object is malformed | ||
*/ | ||
@@ -1254,6 +1429,6 @@ handleResponseObject(object) { | ||
* @param {Object} object | ||
* @returns {Promise} | ||
* @return {Promise} | ||
*/ | ||
sendObject(object) { | ||
if (this[PRIVATE].ended) { | ||
if (this.ended) { | ||
return Promise.reject(new Error('The Readable side of this Duplex stream has ended, unable to send any request objects')); | ||
@@ -1282,2 +1457,75 @@ } | ||
} | ||
/** | ||
* Start pinging our peer periodically. | ||
* | ||
* A ping is a remote method call with the name `this.pingMethod`. This method is called every `this.pingInterval`, | ||
* with a timeout of `this.pingTimeout`. The events 'pingSuccess' and 'pingFail' are emitted based on the results | ||
* of the call. The property `this.isSendingPings` can be read to find out if pings are currently being sent. | ||
* | ||
* When you are done, make sure to either call stopPinging() or end/finish this stream if pinging is enabled, | ||
* otherwise you will leak resources. | ||
*/ | ||
startPinging() { | ||
if (this.ended || this.finished) { | ||
throw Error('startPinging(): This stream has been ended or finished'); | ||
} | ||
// never run >1 timers at once | ||
this.stopPinging(); | ||
this[PRIVATE].isSendingPings = true; | ||
this[PRIVATE].pingTimer = this.setTimeout(() => { | ||
this[PRIVATE].pingTimer = 0; | ||
const {pingNow} = this[PRIVATE]; | ||
const begin = pingNow(); // note: do not set `this` | ||
this.call({ | ||
name: this.pingMethod, | ||
timeout: this.pingTimeout, | ||
}).then( | ||
() => { | ||
const end = pingNow(); | ||
this[PRIVATE].pingConsecutiveFails = 0; | ||
try { | ||
this.emit('pingSuccess', end - begin); | ||
} | ||
finally { | ||
if (this.isSendingPings) { | ||
this.startPinging(); | ||
} | ||
} | ||
}, | ||
error => { | ||
++this[PRIVATE].pingConsecutiveFails; | ||
try { | ||
this.emit('pingFail', this[PRIVATE].pingConsecutiveFails, error); | ||
} | ||
finally { | ||
if (this.isSendingPings) { | ||
this.startPinging(); | ||
} | ||
} | ||
}); | ||
}, this.pingInterval); | ||
} | ||
/** | ||
* Stop the periodic ping (if previously enabled by `startPinging()`). | ||
*/ | ||
stopPinging() { | ||
const {pingTimer} = this[PRIVATE]; | ||
if (pingTimer) { | ||
this.clearTimeout(pingTimer); | ||
this[PRIVATE].pingTimer = 0; | ||
} | ||
this[PRIVATE].isSendingPings = false; | ||
} | ||
} | ||
@@ -1284,0 +1532,0 @@ |
{ | ||
"name": "jsonbird", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "JSON-RPC 2.0 client/server/peer for any reliable transport. Inter-process communication. REST. WebSocket. WebWorker. Out of order messaging or in-order byte streams", | ||
"main": "lib/JSONBird.js", | ||
"scripts": { | ||
"pretest": "eslint lib test && jscs lib test", | ||
"pretest": "eslint lib test", | ||
"test": "istanbul cover node_modules/mocha/bin/_mocha test", | ||
@@ -39,8 +39,7 @@ "preci": "npm run pretest", | ||
"devDependencies": { | ||
"babel-eslint": "^7.1.1", | ||
"chai": "^3.5.0", | ||
"coveralls": "^2.11.15", | ||
"grunt": "^1.0.1", | ||
"grunt-cli": "^1.2.0", | ||
"grunt-eslint": "^18.1.0", | ||
"grunt-jscs": "^3.0.0", | ||
"eslint": "^3.13.1", | ||
"eslint-plugin-import": "^2.2.0", | ||
"istanbul": "^0.4.4", | ||
@@ -47,0 +46,0 @@ "jsdoc-to-markdown": "^1.3.6", |
187
README.md
# JSONBird | ||
[![Travis CI Build Status](https://api.travis-ci.org/Joris-van-der-Wel/jsonbird.svg?branch=master)](https://travis-ci.org/Joris-van-der-Wel/jsonbird) [![Coverage Status](https://coveralls.io/repos/github/Joris-van-der-Wel/jsonbird/badge.svg?branch=master)](https://coveralls.io/github/Joris-van-der-Wel/jsonbird?branch=master) | ||
JSONBird is a Duplex stream which makes it easy to create a flexible JSON-RPC 2.0 client or server (or a bidirectional combination) over any reliable transport. You can use out of order messaging or an in-order byte stream. | ||
@@ -133,2 +131,6 @@ | ||
* _instance_ | ||
* [.setTimeout](#JSONBird+setTimeout) ⇒ <code>function</code> | ||
* [.clearTimeout](#JSONBird+clearTimeout) ⇒ <code>function</code> | ||
* [.ended](#JSONBird+ended) ⇒ <code>boolean</code> | ||
* [.finished](#JSONBird+finished) ⇒ <code>boolean</code> | ||
* [.sessionId](#JSONBird+sessionId) ⇒ <code>string</code> | ||
@@ -150,2 +152,9 @@ * [.writableMode](#JSONBird+writableMode) ⇒ <code>string</code> | ||
* [.defaultTimeout](#JSONBird+defaultTimeout) | ||
* [.pingReceive](#JSONBird+pingReceive) ⇒ <code>boolean</code> | ||
* [.isSendingPings](#JSONBird+isSendingPings) ⇒ <code>boolean</code> | ||
* [.pingMethod](#JSONBird+pingMethod) ⇒ <code>string</code> | ||
* [.pingInterval](#JSONBird+pingInterval) ⇒ <code>number</code> | ||
* [.pingInterval](#JSONBird+pingInterval) | ||
* [.pingTimeout](#JSONBird+pingTimeout) ⇒ <code>number</code> | ||
* [.pingTimeout](#JSONBird+pingTimeout) | ||
* [.generateId()](#JSONBird+generateId) ⇒ <code>string</code> | <code>number</code> | ||
@@ -162,2 +171,8 @@ * [.waitForPendingResponses()](#JSONBird+waitForPendingResponses) ⇒ <code>Promise</code> | ||
* [.bindNotify(nameOrOptions)](#JSONBird+bindNotify) ⇒ <code>function</code> | ||
* [.startPinging()](#JSONBird+startPinging) | ||
* [.stopPinging()](#JSONBird+stopPinging) | ||
* ["error" (error)](#JSONBird+event_error) | ||
* ["protocolError" (error)](#JSONBird+event_protocolError) | ||
* ["pingSuccess" (delay)](#JSONBird+event_pingSuccess) | ||
* ["pingFail" (consecutiveFails, error)](#JSONBird+event_pingFail) | ||
* _static_ | ||
@@ -187,3 +202,44 @@ * [.errorToResponseObject(error, [includeErrorStack])](#JSONBird.errorToResponseObject) ⇒ <code>Object</code> | ||
| [optionsArg.finishOnEnd] | <code>boolean</code> | <code>true</code> | | | ||
| [optionsArg.pingReceive] | <code>boolean</code> | <code>true</code> | | | ||
| [optionsArg.pingMethod] | <code>string</code> | <code>"'jsonbird.ping'"</code> | | | ||
| [optionsArg.pingInterval] | <code>number</code> | <code>2000</code> | | | ||
| [optionsArg.pingTimeout] | <code>number</code> | <code>1000</code> | | | ||
| [optionsArg.pingNow] | <code>number</code> | <code>Date.now</code> | Timer function used to figure out ping delays | | ||
| [optionsArg.setTimeout] | <code>function</code> | <code>global.setTimeout</code> | | | ||
| [optionsArg.clearTimeout] | <code>function</code> | <code>global.clearTimeout</code> | | | ||
<a name="JSONBird+setTimeout"></a> | ||
### jsonBird.setTimeout ⇒ <code>function</code> | ||
The HTML setTimeout function | ||
This function may be overridden for unit tests | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
**Returns**: <code>function</code> - https://html.spec.whatwg.org/#dom-settimeout | ||
<a name="JSONBird+clearTimeout"></a> | ||
### jsonBird.clearTimeout ⇒ <code>function</code> | ||
The HTML clearTimeout function | ||
This function may be overridden for unit tests | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
**Returns**: <code>function</code> - https://html.spec.whatwg.org/#dom-cleartimeout | ||
<a name="JSONBird+ended"></a> | ||
### jsonBird.ended ⇒ <code>boolean</code> | ||
Has the readable side of this duplex stream been ended? | ||
(has the 'end' event been emitted) | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+finished"></a> | ||
### jsonBird.finished ⇒ <code>boolean</code> | ||
Has the writable side of this duplex stream been finished? | ||
(has the 'finish' event been emitted) | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+sessionId"></a> | ||
@@ -378,2 +434,60 @@ | ||
<a name="JSONBird+pingReceive"></a> | ||
### jsonBird.pingReceive ⇒ <code>boolean</code> | ||
If `true` a method with the name `this.pingMethod` is added which simply returns true as fast as possible. | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+isSendingPings"></a> | ||
### jsonBird.isSendingPings ⇒ <code>boolean</code> | ||
Are we currently sending pings to our peer? | ||
In other words, has `this.startPinging()` been called? | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+pingMethod"></a> | ||
### jsonBird.pingMethod ⇒ <code>string</code> | ||
The method name used when receiving or sending pings. | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+pingInterval"></a> | ||
### jsonBird.pingInterval ⇒ <code>number</code> | ||
The time (in milliseconds) between each ping if `isSendingPings` is true. | ||
This time is in addition to the time spent waiting for the previous ping to settle. | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
**Returns**: <code>number</code> - milliseconds | ||
<a name="JSONBird+pingInterval"></a> | ||
### jsonBird.pingInterval | ||
The time (in milliseconds) between each ping if `isSendingPings` is true. | ||
This time is in addition to the time spent waiting for the previous ping to settle. | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| value | <code>number</code> | milliseconds | | ||
<a name="JSONBird+pingTimeout"></a> | ||
### jsonBird.pingTimeout ⇒ <code>number</code> | ||
The maximum amount of time (in milliseconds) to wait for a ping method call to resolve. | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
**Returns**: <code>number</code> - milliseconds | ||
<a name="JSONBird+pingTimeout"></a> | ||
### jsonBird.pingTimeout | ||
The maximum amount of time (in milliseconds) to wait for a ping method call to resolve. | ||
**Kind**: instance property of <code>[JSONBird](#JSONBird)</code> | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| value | <code>number</code> | milliseconds | | ||
<a name="JSONBird+generateId"></a> | ||
@@ -551,2 +665,71 @@ | ||
<a name="JSONBird+startPinging"></a> | ||
### jsonBird.startPinging() | ||
Start pinging our peer periodically. | ||
A ping is a remote method call with the name `this.pingMethod`. This method is called every `this.pingInterval`, | ||
with a timeout of `this.pingTimeout`. The events 'pingSuccess' and 'pingFail' are emitted based on the results | ||
of the call. The property `this.isSendingPings` can be read to find out if pings are currently being sent. | ||
When you are done, make sure to either call stopPinging() or end/finish this stream if pinging is enabled, | ||
otherwise you will leak resources. | ||
**Kind**: instance method of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+stopPinging"></a> | ||
### jsonBird.stopPinging() | ||
Stop the periodic ping (if previously enabled by `startPinging()`). | ||
**Kind**: instance method of <code>[JSONBird](#JSONBird)</code> | ||
<a name="JSONBird+event_error"></a> | ||
### "error" (error) | ||
This event is fired if an uncaught error occurred | ||
Most errors end up at the caller of our functions or at the remote peer, instead of this event. | ||
Note that if you do not listen for this event on node.js, your process might exit. | ||
**Kind**: event emitted by <code>[JSONBird](#JSONBird)</code> | ||
| Param | Type | | ||
| --- | --- | | ||
| error | <code>Error</code> | | ||
<a name="JSONBird+event_protocolError"></a> | ||
### "protocolError" (error) | ||
This event is fired if our peer sent us something that we were unable to parse. | ||
These kind of errors do not end up at the 'error' event | ||
**Kind**: event emitted by <code>[JSONBird](#JSONBird)</code> | ||
| Param | Type | | ||
| --- | --- | | ||
| error | <code>Error</code> | | ||
<a name="JSONBird+event_pingSuccess"></a> | ||
### "pingSuccess" (delay) | ||
The most recent ping sent to our peer succeeded | ||
**Kind**: event emitted by <code>[JSONBird](#JSONBird)</code> | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| delay | <code>number</code> | How long the ping took to resolve (in milliseconds) | | ||
<a name="JSONBird+event_pingFail"></a> | ||
### "pingFail" (consecutiveFails, error) | ||
The most recent ping sent to our peer timed out or resulted in an error | ||
**Kind**: event emitted by <code>[JSONBird](#JSONBird)</code> | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| consecutiveFails | <code>number</code> | The amount of consecutive pings that failed | | ||
| error | <code>Error</code> | | | ||
<a name="JSONBird.errorToResponseObject"></a> | ||
@@ -553,0 +736,0 @@ |
@@ -8,2 +8,3 @@ /* eslint prefer-rest-params: 'off' */ | ||
const through = require('through2'); | ||
const Wait = require('./Wait'); | ||
@@ -10,0 +11,0 @@ const JSONBird = require('../lib/JSONBird'); |
@@ -8,2 +8,3 @@ /* eslint prefer-rest-params: 'off' */ | ||
const through = require('through2'); | ||
const Wait = require('./Wait'); | ||
@@ -10,0 +11,0 @@ const JSONBird = require('../lib/JSONBird'); |
@@ -8,2 +8,3 @@ /* eslint prefer-rest-params: 'off' */ | ||
const through = require('through2'); | ||
const PromiseFateTracker = require('./PromiseFateTracker'); | ||
@@ -10,0 +11,0 @@ const Wait = require('./Wait'); |
217462
10
4406
801
20