diff options
author | Rebecca Turner <me@re-becca.org> | 2018-05-24 23:31:26 +0300 |
---|---|---|
committer | Rebecca Turner <me@re-becca.org> | 2018-05-24 23:31:38 +0300 |
commit | 0483f5c5deaf18c968a128657923103e49f4e67a (patch) | |
tree | 210f154da2f60938471d96a844dcb32cbda09e2a /node_modules/promise-retry | |
parent | 6c294614d800cb95a49ef1c422fbdde65b0731a2 (diff) |
Flatten dependencies and add dev deps to git
Diffstat (limited to 'node_modules/promise-retry')
25 files changed, 1509 insertions, 0 deletions
diff --git a/node_modules/promise-retry/.editorconfig b/node_modules/promise-retry/.editorconfig new file mode 100644 index 000000000..8bc4f108d --- /dev/null +++ b/node_modules/promise-retry/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[package.json] +indent_size = 2 diff --git a/node_modules/promise-retry/.npmignore b/node_modules/promise-retry/.npmignore new file mode 100644 index 000000000..a22dcdd47 --- /dev/null +++ b/node_modules/promise-retry/.npmignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.* diff --git a/node_modules/promise-retry/.travis.yml b/node_modules/promise-retry/.travis.yml new file mode 100644 index 000000000..f85680370 --- /dev/null +++ b/node_modules/promise-retry/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "0.12" + - "4" + - "5" diff --git a/node_modules/promise-retry/LICENSE b/node_modules/promise-retry/LICENSE new file mode 100644 index 000000000..db5e914de --- /dev/null +++ b/node_modules/promise-retry/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 IndigoUnited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/promise-retry/README.md b/node_modules/promise-retry/README.md new file mode 100644 index 000000000..c50328374 --- /dev/null +++ b/node_modules/promise-retry/README.md @@ -0,0 +1,92 @@ +# node-promise-retry + +[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] + +[npm-url]:https://npmjs.org/package/promise-retry +[downloads-image]:http://img.shields.io/npm/dm/promise-retry.svg +[npm-image]:http://img.shields.io/npm/v/promise-retry.svg +[travis-url]:https://travis-ci.org/IndigoUnited/node-promise-retry +[travis-image]:http://img.shields.io/travis/IndigoUnited/node-promise-retry/master.svg +[david-dm-url]:https://david-dm.org/IndigoUnited/node-promise-retry +[david-dm-image]:https://img.shields.io/david/IndigoUnited/node-promise-retry.svg +[david-dm-dev-url]:https://david-dm.org/IndigoUnited/node-promise-retry#info=devDependencies +[david-dm-dev-image]:https://img.shields.io/david/dev/IndigoUnited/node-promise-retry.svg + +Retries a function that returns a promise, leveraging the power of the [retry](https://github.com/tim-kos/node-retry) module to the promises world. + +There's already some modules that are able to retry functions that return promises but +they were rather difficult to use or do not offer an easy way to do conditional retries. + + +## Installation + +`$ npm install promise-retry` + + +## Usage + +### promiseRetry(fn, [options]) + +Calls `fn` until the returned promise ends up fulfilled or rejected with an error different than +a `retry` error. +The `options` argument is an object which maps to the [retry](https://github.com/tim-kos/node-retry) module options: + +- `retries`: The maximum amount of times to retry the operation. Default is `10`. +- `factor`: The exponential factor to use. Default is `2`. +- `minTimeout`: The number of milliseconds before starting the first retry. Default is `1000`. +- `maxTimeout`: The maximum number of milliseconds between two retries. Default is `Infinity`. +- `randomize`: Randomizes the timeouts by multiplying with a factor between `1` to `2`. Default is `false`. + + +The `fn` function will receive a `retry` function as its first argument that should be called with an error whenever you want to retry `fn`. The `retry` function will always throw an error. +If there's retries left, it will throw a special `retry` error that will be handled internally to call `fn` again. +If there's no retries left, it will throw the actual error passed to it. + +If you prefer, you can pass the options first using the alternative function signature `promiseRetry([options], fn)`. + +## Example +```js +var promiseRetry = require('promise-retry'); + +// Simple example +promiseRetry(function (retry, number) { + console.log('attempt number', number); + + return doSomething() + .catch(retry); +}) +.then(function (value) { + // .. +}, function (err) { + // .. +}); + +// Conditional example +promiseRetry(function (retry, number) { + console.log('attempt number', number); + + return doSomething() + .catch(function (err) { + if (err.code === 'ETIMEDOUT') { + retry(err); + } + + throw err; + }); +}) +.then(function (value) { + // .. +}, function (err) { + // .. +}); +``` + + +## Tests + +`$ npm test` + + +## License + +Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php). diff --git a/node_modules/promise-retry/index.js b/node_modules/promise-retry/index.js new file mode 100644 index 000000000..092f27568 --- /dev/null +++ b/node_modules/promise-retry/index.js @@ -0,0 +1,52 @@ +'use strict'; + +var errcode = require('err-code'); +var retry = require('retry'); + +var hasOwn = Object.prototype.hasOwnProperty; + +function isRetryError(err) { + return err && err.code === 'EPROMISERETRY' && hasOwn.call(err, 'retried'); +} + +function promiseRetry(fn, options) { + var temp; + var operation; + + if (typeof fn === 'object' && typeof options === 'function') { + // Swap options and fn when using alternate signature (options, fn) + temp = options; + options = fn; + fn = temp; + } + + operation = retry.operation(options); + + return new Promise(function (resolve, reject) { + operation.attempt(function (number) { + Promise.resolve() + .then(function () { + return fn(function (err) { + if (isRetryError(err)) { + err = err.retried; + } + + throw errcode('Retrying', 'EPROMISERETRY', { retried: err }); + }, number); + }) + .then(resolve, function (err) { + if (isRetryError(err)) { + err = err.retried; + + if (operation.retry(err || new Error())) { + return; + } + } + + reject(err); + }); + }); + }); +} + +module.exports = promiseRetry; diff --git a/node_modules/promise-retry/node_modules/retry/.npmignore b/node_modules/promise-retry/node_modules/retry/.npmignore new file mode 100644 index 000000000..e7726a071 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/.npmignore @@ -0,0 +1,2 @@ +/node_modules/* +npm-debug.log diff --git a/node_modules/promise-retry/node_modules/retry/License b/node_modules/promise-retry/node_modules/retry/License new file mode 100644 index 000000000..0b58de379 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/License @@ -0,0 +1,21 @@ +Copyright (c) 2011: +Tim Koschützki (tim@debuggable.com) +Felix Geisendörfer (felix@debuggable.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/node_modules/promise-retry/node_modules/retry/Makefile b/node_modules/promise-retry/node_modules/retry/Makefile new file mode 100644 index 000000000..eee21a99d --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/Makefile @@ -0,0 +1,22 @@ +SHELL := /bin/bash + +test: + @node test/runner.js + +release-major: test + npm version major -m "Release %s" + git push + npm publish + +release-minor: test + npm version minor -m "Release %s" + git push + npm publish + +release-patch: test + npm version patch -m "Release %s" + git push + npm publish + +.PHONY: test + diff --git a/node_modules/promise-retry/node_modules/retry/README.md b/node_modules/promise-retry/node_modules/retry/README.md new file mode 100644 index 000000000..eee05f7bb --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/README.md @@ -0,0 +1,215 @@ +# retry + +Abstraction for exponential and custom retry strategies for failed operations. + +## Installation + + npm install retry + +## Current Status + +This module has been tested and is ready to be used. + +## Tutorial + +The example below will retry a potentially failing `dns.resolve` operation +`10` times using an exponential backoff strategy. With the default settings, this +means the last attempt is made after `17 minutes and 3 seconds`. + +``` javascript +var dns = require('dns'); +var retry = require('retry'); + +function faultTolerantResolve(address, cb) { + var operation = retry.operation(); + + operation.attempt(function(currentAttempt) { + dns.resolve(address, function(err, addresses) { + if (operation.retry(err)) { + return; + } + + cb(err ? operation.mainError() : null, addresses); + }); + }); +} + +faultTolerantResolve('nodejs.org', function(err, addresses) { + console.log(err, addresses); +}); +``` + +Of course you can also configure the factors that go into the exponential +backoff. See the API documentation below for all available settings. +currentAttempt is an int representing the number of attempts so far. + +``` javascript +var operation = retry.operation({ + retries: 5, + factor: 3, + minTimeout: 1 * 1000, + maxTimeout: 60 * 1000, + randomize: true, +}); +``` + +## API + +### retry.operation([options]) + +Creates a new `RetryOperation` object. `options` is the same as `retry.timeouts()`'s `options`, with two additions: + +* `forever`: Whether to retry forever, defaults to `false`. +* `unref`: Wether to [unref](https://nodejs.org/api/timers.html#timers_unref) the setTimeout's, defaults to `false`. + +### retry.timeouts([options]) + +Returns an array of timeouts. All time `options` and return values are in +milliseconds. If `options` is an array, a copy of that array is returned. + +`options` is a JS object that can contain any of the following keys: + +* `retries`: The maximum amount of times to retry the operation. Default is `10`. +* `factor`: The exponential factor to use. Default is `2`. +* `minTimeout`: The number of milliseconds before starting the first retry. Default is `1000`. +* `maxTimeout`: The maximum number of milliseconds between two retries. Default is `Infinity`. +* `randomize`: Randomizes the timeouts by multiplying with a factor between `1` to `2`. Default is `false`. + +The formula used to calculate the individual timeouts is: + +``` +Math.min(random * minTimeout * Math.pow(factor, attempt), maxTimeout) +``` + +Have a look at [this article][article] for a better explanation of approach. + +If you want to tune your `factor` / `times` settings to attempt the last retry +after a certain amount of time, you can use wolfram alpha. For example in order +to tune for `10` attempts in `5 minutes`, you can use this equation: + +![screenshot](https://github.com/tim-kos/node-retry/raw/master/equation.gif) + +Explaining the various values from left to right: + +* `k = 0 ... 9`: The `retries` value (10) +* `1000`: The `minTimeout` value in ms (1000) +* `x^k`: No need to change this, `x` will be your resulting factor +* `5 * 60 * 1000`: The desired total amount of time for retrying in ms (5 minutes) + +To make this a little easier for you, use wolfram alpha to do the calculations: + +<http://www.wolframalpha.com/input/?i=Sum%5B1000*x^k%2C+{k%2C+0%2C+9}%5D+%3D+5+*+60+*+1000> + +[article]: http://dthain.blogspot.com/2009/02/exponential-backoff-in-distributed.html + +### retry.createTimeout(attempt, opts) + +Returns a new `timeout` (integer in milliseconds) based on the given parameters. + +`attempt` is an integer representing for which retry the timeout should be calculated. If your retry operation was executed 4 times you had one attempt and 3 retries. If you then want to calculate a new timeout, you should set `attempt` to 4 (attempts are zero-indexed). + +`opts` can include `factor`, `minTimeout`, `randomize` (boolean) and `maxTimeout`. They are documented above. + +`retry.createTimeout()` is used internally by `retry.timeouts()` and is public for you to be able to create your own timeouts for reinserting an item, see [issue #13](https://github.com/tim-kos/node-retry/issues/13). + +### retry.wrap(obj, [options], [methodNames]) + +Wrap all functions of the `obj` with retry. Optionally you can pass operation options and +an array of method names which need to be wrapped. + +``` +retry.wrap(obj) + +retry.wrap(obj, ['method1', 'method2']) + +retry.wrap(obj, {retries: 3}) + +retry.wrap(obj, {retries: 3}, ['method1', 'method2']) +``` +The `options` object can take any options that the usual call to `retry.operation` can take. + +### new RetryOperation(timeouts, [options]) + +Creates a new `RetryOperation` where `timeouts` is an array where each value is +a timeout given in milliseconds. + +Available options: +* `forever`: Whether to retry forever, defaults to `false`. +* `unref`: Wether to [unref](https://nodejs.org/api/timers.html#timers_unref) the setTimeout's, defaults to `false`. + +If `forever` is true, the following changes happen: +* `RetryOperation.errors()` will only output an array of one item: the last error. +* `RetryOperation` will repeatedly use the `timeouts` array. Once all of its timeouts have been used up, it restarts with the first timeout, then uses the second and so on. + +#### retryOperation.errors() + +Returns an array of all errors that have been passed to +`retryOperation.retry()` so far. + +#### retryOperation.mainError() + +A reference to the error object that occured most frequently. Errors are +compared using the `error.message` property. + +If multiple error messages occured the same amount of time, the last error +object with that message is returned. + +If no errors occured so far, the value is `null`. + +#### retryOperation.attempt(fn, timeoutOps) + +Defines the function `fn` that is to be retried and executes it for the first +time right away. The `fn` function can receive an optional `currentAttempt` callback that represents the number of attempts to execute `fn` so far. + +Optionally defines `timeoutOps` which is an object having a property `timeout` in miliseconds and a property `cb` callback function. +Whenever your retry operation takes longer than `timeout` to execute, the timeout callback function `cb` is called. + + +#### retryOperation.try(fn) + +This is an alias for `retryOperation.attempt(fn)`. This is deprecated. Please use `retryOperation.attempt(fn)` instead. + +#### retryOperation.start(fn) + +This is an alias for `retryOperation.attempt(fn)`. This is deprecated. Please use `retryOperation.attempt(fn)` instead. + +#### retryOperation.retry(error) + +Returns `false` when no `error` value is given, or the maximum amount of retries +has been reached. + +Otherwise it returns `true`, and retries the operation after the timeout for +the current attempt number. + +#### retryOperation.stop() + +Allows you to stop the operation being retried. Useful for aborting the operation on a fatal error etc. + +#### retryOperation.attempts() + +Returns an int representing the number of attempts it took to call `fn` before it was successful. + +## License + +retry is licensed under the MIT license. + + +# Changelog + +0.10.0 Adding `stop` functionality, thanks to @maxnachlinger. + +0.9.0 Adding `unref` functionality, thanks to @satazor. + +0.8.0 Implementing retry.wrap. + +0.7.0 Some bug fixes and made retry.createTimeout() public. Fixed issues [#10](https://github.com/tim-kos/node-retry/issues/10), [#12](https://github.com/tim-kos/node-retry/issues/12), and [#13](https://github.com/tim-kos/node-retry/issues/13). + +0.6.0 Introduced optional timeOps parameter for the attempt() function which is an object having a property timeout in milliseconds and a property cb callback function. Whenever your retry operation takes longer than timeout to execute, the timeout callback function cb is called. + +0.5.0 Some minor refactoring. + +0.4.0 Changed retryOperation.try() to retryOperation.attempt(). Deprecated the aliases start() and try() for it. + +0.3.0 Added retryOperation.start() which is an alias for retryOperation.try(). + +0.2.0 Added attempts() function and parameter to retryOperation.try() representing the number of attempts it took to call fn(). diff --git a/node_modules/promise-retry/node_modules/retry/equation.gif b/node_modules/promise-retry/node_modules/retry/equation.gif Binary files differnew file mode 100644 index 000000000..97107237b --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/equation.gif diff --git a/node_modules/promise-retry/node_modules/retry/example/dns.js b/node_modules/promise-retry/node_modules/retry/example/dns.js new file mode 100644 index 000000000..446729b6f --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/example/dns.js @@ -0,0 +1,31 @@ +var dns = require('dns'); +var retry = require('../lib/retry'); + +function faultTolerantResolve(address, cb) { + var opts = { + retries: 2, + factor: 2, + minTimeout: 1 * 1000, + maxTimeout: 2 * 1000, + randomize: true + }; + var operation = retry.operation(opts); + + operation.attempt(function(currentAttempt) { + dns.resolve(address, function(err, addresses) { + if (operation.retry(err)) { + return; + } + + cb(operation.mainError(), operation.errors(), addresses); + }); + }); +} + +faultTolerantResolve('nodejs.org', function(err, errors, addresses) { + console.warn('err:'); + console.log(err); + + console.warn('addresses:'); + console.log(addresses); +});
\ No newline at end of file diff --git a/node_modules/promise-retry/node_modules/retry/example/stop.js b/node_modules/promise-retry/node_modules/retry/example/stop.js new file mode 100644 index 000000000..e1ceafeeb --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/example/stop.js @@ -0,0 +1,40 @@ +var retry = require('../lib/retry'); + +function attemptAsyncOperation(someInput, cb) { + var opts = { + retries: 2, + factor: 2, + minTimeout: 1 * 1000, + maxTimeout: 2 * 1000, + randomize: true + }; + var operation = retry.operation(opts); + + operation.attempt(function(currentAttempt) { + failingAsyncOperation(someInput, function(err, result) { + + if (err && err.message === 'A fatal error') { + operation.stop(); + return cb(err); + } + + if (operation.retry(err)) { + return; + } + + cb(operation.mainError(), operation.errors(), result); + }); + }); +} + +attemptAsyncOperation('test input', function(err, errors, result) { + console.warn('err:'); + console.log(err); + + console.warn('result:'); + console.log(result); +}); + +function failingAsyncOperation(input, cb) { + return setImmediate(cb.bind(null, new Error('A fatal error'))); +} diff --git a/node_modules/promise-retry/node_modules/retry/index.js b/node_modules/promise-retry/node_modules/retry/index.js new file mode 100644 index 000000000..ee62f3a11 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/retry');
\ No newline at end of file diff --git a/node_modules/promise-retry/node_modules/retry/lib/retry.js b/node_modules/promise-retry/node_modules/retry/lib/retry.js new file mode 100644 index 000000000..77428cfd0 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/lib/retry.js @@ -0,0 +1,99 @@ +var RetryOperation = require('./retry_operation'); + +exports.operation = function(options) { + var timeouts = exports.timeouts(options); + return new RetryOperation(timeouts, { + forever: options && options.forever, + unref: options && options.unref + }); +}; + +exports.timeouts = function(options) { + if (options instanceof Array) { + return [].concat(options); + } + + var opts = { + retries: 10, + factor: 2, + minTimeout: 1 * 1000, + maxTimeout: Infinity, + randomize: false + }; + for (var key in options) { + opts[key] = options[key]; + } + + if (opts.minTimeout > opts.maxTimeout) { + throw new Error('minTimeout is greater than maxTimeout'); + } + + var timeouts = []; + for (var i = 0; i < opts.retries; i++) { + timeouts.push(this.createTimeout(i, opts)); + } + + if (options && options.forever && !timeouts.length) { + timeouts.push(this.createTimeout(i, opts)); + } + + // sort the array numerically ascending + timeouts.sort(function(a,b) { + return a - b; + }); + + return timeouts; +}; + +exports.createTimeout = function(attempt, opts) { + var random = (opts.randomize) + ? (Math.random() + 1) + : 1; + + var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt)); + timeout = Math.min(timeout, opts.maxTimeout); + + return timeout; +}; + +exports.wrap = function(obj, options, methods) { + if (options instanceof Array) { + methods = options; + options = null; + } + + if (!methods) { + methods = []; + for (var key in obj) { + if (typeof obj[key] === 'function') { + methods.push(key); + } + } + } + + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + var original = obj[method]; + + obj[method] = function retryWrapper() { + var op = exports.operation(options); + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + + args.push(function(err) { + if (op.retry(err)) { + return; + } + if (err) { + arguments[0] = op.mainError(); + } + callback.apply(this, arguments); + }); + + op.attempt(function() { + original.apply(obj, args); + }); + }; + obj[method].options = options; + } +}; diff --git a/node_modules/promise-retry/node_modules/retry/lib/retry_operation.js b/node_modules/promise-retry/node_modules/retry/lib/retry_operation.js new file mode 100644 index 000000000..2b3db8e17 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/lib/retry_operation.js @@ -0,0 +1,143 @@ +function RetryOperation(timeouts, options) { + // Compatibility for the old (timeouts, retryForever) signature + if (typeof options === 'boolean') { + options = { forever: options }; + } + + this._timeouts = timeouts; + this._options = options || {}; + this._fn = null; + this._errors = []; + this._attempts = 1; + this._operationTimeout = null; + this._operationTimeoutCb = null; + this._timeout = null; + + if (this._options.forever) { + this._cachedTimeouts = this._timeouts.slice(0); + } +} +module.exports = RetryOperation; + +RetryOperation.prototype.stop = function() { + if (this._timeout) { + clearTimeout(this._timeout); + } + + this._timeouts = []; + this._cachedTimeouts = null; +}; + +RetryOperation.prototype.retry = function(err) { + if (this._timeout) { + clearTimeout(this._timeout); + } + + if (!err) { + return false; + } + + this._errors.push(err); + + var timeout = this._timeouts.shift(); + if (timeout === undefined) { + if (this._cachedTimeouts) { + // retry forever, only keep last error + this._errors.splice(this._errors.length - 1, this._errors.length); + this._timeouts = this._cachedTimeouts.slice(0); + timeout = this._timeouts.shift(); + } else { + return false; + } + } + + var self = this; + var timer = setTimeout(function() { + self._attempts++; + + if (self._operationTimeoutCb) { + self._timeout = setTimeout(function() { + self._operationTimeoutCb(self._attempts); + }, self._operationTimeout); + + if (this._options.unref) { + self._timeout.unref(); + } + } + + self._fn(self._attempts); + }, timeout); + + if (this._options.unref) { + timer.unref(); + } + + return true; +}; + +RetryOperation.prototype.attempt = function(fn, timeoutOps) { + this._fn = fn; + + if (timeoutOps) { + if (timeoutOps.timeout) { + this._operationTimeout = timeoutOps.timeout; + } + if (timeoutOps.cb) { + this._operationTimeoutCb = timeoutOps.cb; + } + } + + var self = this; + if (this._operationTimeoutCb) { + this._timeout = setTimeout(function() { + self._operationTimeoutCb(); + }, self._operationTimeout); + } + + this._fn(this._attempts); +}; + +RetryOperation.prototype.try = function(fn) { + console.log('Using RetryOperation.try() is deprecated'); + this.attempt(fn); +}; + +RetryOperation.prototype.start = function(fn) { + console.log('Using RetryOperation.start() is deprecated'); + this.attempt(fn); +}; + +RetryOperation.prototype.start = RetryOperation.prototype.try; + +RetryOperation.prototype.errors = function() { + return this._errors; +}; + +RetryOperation.prototype.attempts = function() { + return this._attempts; +}; + +RetryOperation.prototype.mainError = function() { + if (this._errors.length === 0) { + return null; + } + + var counts = {}; + var mainError = null; + var mainErrorCount = 0; + + for (var i = 0; i < this._errors.length; i++) { + var error = this._errors[i]; + var message = error.message; + var count = (counts[message] || 0) + 1; + + counts[message] = count; + + if (count >= mainErrorCount) { + mainError = error; + mainErrorCount = count; + } + } + + return mainError; +}; diff --git a/node_modules/promise-retry/node_modules/retry/package.json b/node_modules/promise-retry/node_modules/retry/package.json new file mode 100644 index 000000000..7d6b1f99f --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/package.json @@ -0,0 +1,56 @@ +{ + "_from": "retry@^0.10.0", + "_id": "retry@0.10.1", + "_inBundle": false, + "_integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "_location": "/promise-retry/retry", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "retry@^0.10.0", + "name": "retry", + "escapedName": "retry", + "rawSpec": "^0.10.0", + "saveSpec": null, + "fetchSpec": "^0.10.0" + }, + "_requiredBy": [ + "/promise-retry" + ], + "_resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "_shasum": "e76388d217992c252750241d3d3956fed98d8ff4", + "_spec": "retry@^0.10.0", + "_where": "/Users/rebecca/code/npm/node_modules/promise-retry", + "author": { + "name": "Tim Koschützki", + "email": "tim@debuggable.com", + "url": "http://debuggable.com/" + }, + "bugs": { + "url": "https://github.com/tim-kos/node-retry/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Abstraction for exponential and custom retry strategies for failed operations.", + "devDependencies": { + "fake": "0.2.0", + "far": "0.0.1" + }, + "directories": { + "lib": "./lib" + }, + "engines": { + "node": "*" + }, + "homepage": "https://github.com/tim-kos/node-retry", + "license": "MIT", + "main": "index", + "name": "retry", + "repository": { + "type": "git", + "url": "git://github.com/tim-kos/node-retry.git" + }, + "version": "0.10.1" +} diff --git a/node_modules/promise-retry/node_modules/retry/test/common.js b/node_modules/promise-retry/node_modules/retry/test/common.js new file mode 100644 index 000000000..224720696 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/test/common.js @@ -0,0 +1,10 @@ +var common = module.exports; +var path = require('path'); + +var rootDir = path.join(__dirname, '..'); +common.dir = { + lib: rootDir + '/lib' +}; + +common.assert = require('assert'); +common.fake = require('fake');
\ No newline at end of file diff --git a/node_modules/promise-retry/node_modules/retry/test/integration/test-forever.js b/node_modules/promise-retry/node_modules/retry/test/integration/test-forever.js new file mode 100644 index 000000000..b41307cb5 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/test/integration/test-forever.js @@ -0,0 +1,24 @@ +var common = require('../common'); +var assert = common.assert; +var retry = require(common.dir.lib + '/retry'); + +(function testForeverUsesFirstTimeout() { + var operation = retry.operation({ + retries: 0, + minTimeout: 100, + maxTimeout: 100, + forever: true + }); + + operation.attempt(function(numAttempt) { + console.log('>numAttempt', numAttempt); + var err = new Error("foo"); + if (numAttempt == 10) { + operation.stop(); + } + + if (operation.retry(err)) { + return; + } + }); +})(); diff --git a/node_modules/promise-retry/node_modules/retry/test/integration/test-retry-operation.js b/node_modules/promise-retry/node_modules/retry/test/integration/test-retry-operation.js new file mode 100644 index 000000000..916936424 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/test/integration/test-retry-operation.js @@ -0,0 +1,176 @@ +var common = require('../common'); +var assert = common.assert; +var fake = common.fake.create(); +var retry = require(common.dir.lib + '/retry'); + +(function testErrors() { + var operation = retry.operation(); + + var error = new Error('some error'); + var error2 = new Error('some other error'); + operation._errors.push(error); + operation._errors.push(error2); + + assert.deepEqual(operation.errors(), [error, error2]); +})(); + +(function testMainErrorReturnsMostFrequentError() { + var operation = retry.operation(); + var error = new Error('some error'); + var error2 = new Error('some other error'); + + operation._errors.push(error); + operation._errors.push(error2); + operation._errors.push(error); + + assert.strictEqual(operation.mainError(), error); +})(); + +(function testMainErrorReturnsLastErrorOnEqualCount() { + var operation = retry.operation(); + var error = new Error('some error'); + var error2 = new Error('some other error'); + + operation._errors.push(error); + operation._errors.push(error2); + + assert.strictEqual(operation.mainError(), error2); +})(); + +(function testAttempt() { + var operation = retry.operation(); + var fn = new Function(); + + var timeoutOpts = { + timeout: 1, + cb: function() {} + }; + operation.attempt(fn, timeoutOpts); + + assert.strictEqual(fn, operation._fn); + assert.strictEqual(timeoutOpts.timeout, operation._operationTimeout); + assert.strictEqual(timeoutOpts.cb, operation._operationTimeoutCb); +})(); + +(function testRetry() { + var times = 3; + var error = new Error('some error'); + var operation = retry.operation([1, 2, 3]); + var attempts = 0; + + var finalCallback = fake.callback('finalCallback'); + fake.expectAnytime(finalCallback); + + var fn = function() { + operation.attempt(function(currentAttempt) { + attempts++; + assert.equal(currentAttempt, attempts); + if (operation.retry(error)) { + return; + } + + assert.strictEqual(attempts, 4); + assert.strictEqual(operation.attempts(), attempts); + assert.strictEqual(operation.mainError(), error); + finalCallback(); + }); + }; + + fn(); +})(); + +(function testRetryForever() { + var error = new Error('some error'); + var operation = retry.operation({ retries: 3, forever: true }); + var attempts = 0; + + var finalCallback = fake.callback('finalCallback'); + fake.expectAnytime(finalCallback); + + var fn = function() { + operation.attempt(function(currentAttempt) { + attempts++; + assert.equal(currentAttempt, attempts); + if (attempts !== 6 && operation.retry(error)) { + return; + } + + assert.strictEqual(attempts, 6); + assert.strictEqual(operation.attempts(), attempts); + assert.strictEqual(operation.mainError(), error); + finalCallback(); + }); + }; + + fn(); +})(); + +(function testRetryForeverNoRetries() { + var error = new Error('some error'); + var delay = 50 + var operation = retry.operation({ + retries: null, + forever: true, + minTimeout: delay, + maxTimeout: delay + }); + + var attempts = 0; + var startTime = new Date().getTime(); + + var finalCallback = fake.callback('finalCallback'); + fake.expectAnytime(finalCallback); + + var fn = function() { + operation.attempt(function(currentAttempt) { + attempts++; + assert.equal(currentAttempt, attempts); + if (attempts !== 4 && operation.retry(error)) { + return; + } + + var endTime = new Date().getTime(); + var minTime = startTime + (delay * 3); + var maxTime = minTime + 20 // add a little headroom for code execution time + assert(endTime > minTime) + assert(endTime < maxTime) + assert.strictEqual(attempts, 4); + assert.strictEqual(operation.attempts(), attempts); + assert.strictEqual(operation.mainError(), error); + finalCallback(); + }); + }; + + fn(); +})(); + +(function testStop() { + var error = new Error('some error'); + var operation = retry.operation([1, 2, 3]); + var attempts = 0; + + var finalCallback = fake.callback('finalCallback'); + fake.expectAnytime(finalCallback); + + var fn = function() { + operation.attempt(function(currentAttempt) { + attempts++; + assert.equal(currentAttempt, attempts); + + if (attempts === 2) { + operation.stop(); + + assert.strictEqual(attempts, 2); + assert.strictEqual(operation.attempts(), attempts); + assert.strictEqual(operation.mainError(), error); + finalCallback(); + } + + if (operation.retry(error)) { + return; + } + }); + }; + + fn(); +})(); diff --git a/node_modules/promise-retry/node_modules/retry/test/integration/test-retry-wrap.js b/node_modules/promise-retry/node_modules/retry/test/integration/test-retry-wrap.js new file mode 100644 index 000000000..7ca8bc7eb --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/test/integration/test-retry-wrap.js @@ -0,0 +1,77 @@ +var common = require('../common'); +var assert = common.assert; +var fake = common.fake.create(); +var retry = require(common.dir.lib + '/retry'); + +function getLib() { + return { + fn1: function() {}, + fn2: function() {}, + fn3: function() {} + }; +} + +(function wrapAll() { + var lib = getLib(); + retry.wrap(lib); + assert.equal(lib.fn1.name, 'retryWrapper'); + assert.equal(lib.fn2.name, 'retryWrapper'); + assert.equal(lib.fn3.name, 'retryWrapper'); +}()); + +(function wrapAllPassOptions() { + var lib = getLib(); + retry.wrap(lib, {retries: 2}); + assert.equal(lib.fn1.name, 'retryWrapper'); + assert.equal(lib.fn2.name, 'retryWrapper'); + assert.equal(lib.fn3.name, 'retryWrapper'); + assert.equal(lib.fn1.options.retries, 2); + assert.equal(lib.fn2.options.retries, 2); + assert.equal(lib.fn3.options.retries, 2); +}()); + +(function wrapDefined() { + var lib = getLib(); + retry.wrap(lib, ['fn2', 'fn3']); + assert.notEqual(lib.fn1.name, 'retryWrapper'); + assert.equal(lib.fn2.name, 'retryWrapper'); + assert.equal(lib.fn3.name, 'retryWrapper'); +}()); + +(function wrapDefinedAndPassOptions() { + var lib = getLib(); + retry.wrap(lib, {retries: 2}, ['fn2', 'fn3']); + assert.notEqual(lib.fn1.name, 'retryWrapper'); + assert.equal(lib.fn2.name, 'retryWrapper'); + assert.equal(lib.fn3.name, 'retryWrapper'); + assert.equal(lib.fn2.options.retries, 2); + assert.equal(lib.fn3.options.retries, 2); +}()); + +(function runWrappedWithoutError() { + var callbackCalled; + var lib = {method: function(a, b, callback) { + assert.equal(a, 1); + assert.equal(b, 2); + assert.equal(typeof callback, 'function'); + callback(); + }}; + retry.wrap(lib); + lib.method(1, 2, function() { + callbackCalled = true; + }); + assert.ok(callbackCalled); +}()); + +(function runWrappedWithError() { + var callbackCalled; + var lib = {method: function(callback) { + callback(new Error('Some error')); + }}; + retry.wrap(lib, {retries: 1}); + lib.method(function(err) { + callbackCalled = true; + assert.ok(err instanceof Error); + }); + assert.ok(!callbackCalled); +}()); diff --git a/node_modules/promise-retry/node_modules/retry/test/integration/test-timeouts.js b/node_modules/promise-retry/node_modules/retry/test/integration/test-timeouts.js new file mode 100644 index 000000000..7206b0fb0 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/test/integration/test-timeouts.js @@ -0,0 +1,69 @@ +var common = require('../common'); +var assert = common.assert; +var retry = require(common.dir.lib + '/retry'); + +(function testDefaultValues() { + var timeouts = retry.timeouts(); + + assert.equal(timeouts.length, 10); + assert.equal(timeouts[0], 1000); + assert.equal(timeouts[1], 2000); + assert.equal(timeouts[2], 4000); +})(); + +(function testDefaultValuesWithRandomize() { + var minTimeout = 5000; + var timeouts = retry.timeouts({ + minTimeout: minTimeout, + randomize: true + }); + + assert.equal(timeouts.length, 10); + assert.ok(timeouts[0] > minTimeout); + assert.ok(timeouts[1] > timeouts[0]); + assert.ok(timeouts[2] > timeouts[1]); +})(); + +(function testPassedTimeoutsAreUsed() { + var timeoutsArray = [1000, 2000, 3000]; + var timeouts = retry.timeouts(timeoutsArray); + assert.deepEqual(timeouts, timeoutsArray); + assert.notStrictEqual(timeouts, timeoutsArray); +})(); + +(function testTimeoutsAreWithinBoundaries() { + var minTimeout = 1000; + var maxTimeout = 10000; + var timeouts = retry.timeouts({ + minTimeout: minTimeout, + maxTimeout: maxTimeout + }); + for (var i = 0; i < timeouts; i++) { + assert.ok(timeouts[i] >= minTimeout); + assert.ok(timeouts[i] <= maxTimeout); + } +})(); + +(function testTimeoutsAreIncremental() { + var timeouts = retry.timeouts(); + var lastTimeout = timeouts[0]; + for (var i = 0; i < timeouts; i++) { + assert.ok(timeouts[i] > lastTimeout); + lastTimeout = timeouts[i]; + } +})(); + +(function testTimeoutsAreIncrementalForFactorsLessThanOne() { + var timeouts = retry.timeouts({ + retries: 3, + factor: 0.5 + }); + + var expected = [250, 500, 1000]; + assert.deepEqual(expected, timeouts); +})(); + +(function testRetries() { + var timeouts = retry.timeouts({retries: 2}); + assert.strictEqual(timeouts.length, 2); +})(); diff --git a/node_modules/promise-retry/node_modules/retry/test/runner.js b/node_modules/promise-retry/node_modules/retry/test/runner.js new file mode 100644 index 000000000..e0ee2f570 --- /dev/null +++ b/node_modules/promise-retry/node_modules/retry/test/runner.js @@ -0,0 +1,5 @@ +var far = require('far').create(); + +far.add(__dirname); +far.include(/\/test-.*\.js$/); +far.execute(); diff --git a/node_modules/promise-retry/package.json b/node_modules/promise-retry/package.json new file mode 100644 index 000000000..1e2f40d71 --- /dev/null +++ b/node_modules/promise-retry/package.json @@ -0,0 +1,70 @@ +{ + "_from": "promise-retry@^1.1.1", + "_id": "promise-retry@1.1.1", + "_inBundle": false, + "_integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "_location": "/promise-retry", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "promise-retry@^1.1.1", + "name": "promise-retry", + "escapedName": "promise-retry", + "rawSpec": "^1.1.1", + "saveSpec": null, + "fetchSpec": "^1.1.1" + }, + "_requiredBy": [ + "/make-fetch-happen", + "/npm-profile/make-fetch-happen", + "/npm-registry-fetch/make-fetch-happen", + "/pacote" + ], + "_resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "_shasum": "6739e968e3051da20ce6497fb2b50f6911df3d6d", + "_spec": "promise-retry@^1.1.1", + "_where": "/Users/rebecca/code/npm/node_modules/pacote", + "author": { + "name": "IndigoUnited", + "email": "hello@indigounited.com", + "url": "http://indigounited.com" + }, + "bugs": { + "url": "https://github.com/IndigoUnited/node-promise-retry/issues/" + }, + "bundleDependencies": false, + "dependencies": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "deprecated": false, + "description": "Retries a function that returns a promise, leveraging the power of the retry module.", + "devDependencies": { + "expect.js": "^0.3.1", + "mocha": "^3.0.0", + "sleep-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.12" + }, + "homepage": "https://github.com/IndigoUnited/node-promise-retry#readme", + "keywords": [ + "retry", + "promise", + "backoff", + "repeat", + "replay" + ], + "license": "MIT", + "main": "index.js", + "name": "promise-retry", + "repository": { + "type": "git", + "url": "git://github.com/IndigoUnited/node-promise-retry.git" + }, + "scripts": { + "test": "mocha --bail -t 10000" + }, + "version": "1.1.1" +} diff --git a/node_modules/promise-retry/test/test.js b/node_modules/promise-retry/test/test.js new file mode 100644 index 000000000..466b0991e --- /dev/null +++ b/node_modules/promise-retry/test/test.js @@ -0,0 +1,263 @@ +'use strict'; + +var expect = require('expect.js'); +var promiseRetry = require('../'); +var promiseDelay = require('sleep-promise'); + +describe('promise-retry', function () { + it('should call fn again if retry was called', function () { + var count = 0; + + return promiseRetry(function (retry) { + count += 1; + + return promiseDelay(10) + .then(function () { + if (count <= 2) { + retry(new Error('foo')); + } + + return 'final'; + }); + }, { factor: 1 }) + .then(function (value) { + expect(value).to.be('final'); + expect(count).to.be(3); + }, function () { + throw new Error('should not fail'); + }); + }); + + it('should call fn with the attempt number', function () { + var count = 0; + + return promiseRetry(function (retry, number) { + count += 1; + expect(count).to.equal(number); + + return promiseDelay(10) + .then(function () { + if (count <= 2) { + retry(new Error('foo')); + } + + return 'final'; + }); + }, { factor: 1 }) + .then(function (value) { + expect(value).to.be('final'); + expect(count).to.be(3); + }, function () { + throw new Error('should not fail'); + }); + }); + + it('should not retry on fulfillment if retry was not called', function () { + var count = 0; + + return promiseRetry(function () { + count += 1; + + return promiseDelay(10) + .then(function () { + return 'final'; + }); + }) + .then(function (value) { + expect(value).to.be('final'); + expect(count).to.be(1); + }, function () { + throw new Error('should not fail'); + }); + }); + + it('should not retry on rejection if retry was not called', function () { + var count = 0; + + return promiseRetry(function () { + count += 1; + + return promiseDelay(10) + .then(function () { + throw new Error('foo'); + }); + }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err.message).to.be('foo'); + expect(count).to.be(1); + }); + }); + + it('should not retry on rejection if nr of retries is 0', function () { + var count = 0; + + return promiseRetry(function (retry) { + count += 1; + + return promiseDelay(10) + .then(function () { + throw new Error('foo'); + }) + .catch(retry); + }, { retries : 0 }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err.message).to.be('foo'); + expect(count).to.be(1); + }); + }); + + it('should reject the promise if the retries were exceeded', function () { + var count = 0; + + return promiseRetry(function (retry) { + count += 1; + + return promiseDelay(10) + .then(function () { + throw new Error('foo'); + }) + .catch(retry); + }, { retries: 2, factor: 1 }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err.message).to.be('foo'); + expect(count).to.be(3); + }); + }); + + it('should pass options to the underlying retry module', function () { + var count = 0; + + return promiseRetry(function (retry) { + return promiseDelay(10) + .then(function () { + if (count < 2) { + count += 1; + retry(new Error('foo')); + } + + return 'final'; + }); + }, { retries: 1, factor: 1 }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err.message).to.be('foo'); + }); + }); + + it('should convert direct fulfillments into promises', function () { + return promiseRetry(function () { + return 'final'; + }, { factor: 1 }) + .then(function (value) { + expect(value).to.be('final'); + }, function () { + throw new Error('should not fail'); + }); + }); + + it('should convert direct rejections into promises', function () { + promiseRetry(function () { + throw new Error('foo'); + }, { retries: 1, factor: 1 }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err.message).to.be('foo'); + }); + }); + + it('should not crash on undefined rejections', function () { + return promiseRetry(function () { + throw undefined; + }, { retries: 1, factor: 1 }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err).to.be(undefined); + }) + .then(function () { + return promiseRetry(function (retry) { + retry(); + }, { retries: 1, factor: 1 }); + }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err).to.be(undefined); + }); + }); + + it('should retry if retry() was called with undefined', function () { + var count = 0; + + return promiseRetry(function (retry) { + count += 1; + + return promiseDelay(10) + .then(function () { + if (count <= 2) { + retry(); + } + + return 'final'; + }); + }, { factor: 1 }) + .then(function (value) { + expect(value).to.be('final'); + expect(count).to.be(3); + }, function () { + throw new Error('should not fail'); + }); + }); + + it('should work with several retries in the same chain', function () { + var count = 0; + + return promiseRetry(function (retry) { + count += 1; + + return promiseDelay(10) + .then(function () { + retry(new Error('foo')); + }) + .catch(function (err) { + retry(err); + }); + }, { retries: 1, factor: 1 }) + .then(function () { + throw new Error('should not succeed'); + }, function (err) { + expect(err.message).to.be('foo'); + expect(count).to.be(2); + }); + }); + + it('should allow options to be passed first', function () { + var count = 0; + + return promiseRetry({ factor: 1 }, function (retry) { + count += 1; + + return promiseDelay(10) + .then(function () { + if (count <= 2) { + retry(new Error('foo')); + } + + return 'final'; + }); + }).then(function (value) { + expect(value).to.be('final'); + expect(count).to.be(3); + }, function () { + throw new Error('should not fail'); + }); + }); +}); |