bottleneck
Advanced tools
Comparing version 2.6.0 to 2.7.0
@@ -56,2 +56,7 @@ declare module "bottleneck" { | ||
readonly clearDatastore?: boolean; | ||
/** | ||
* The Redis TTL in milliseconds for the keys created by the limiter. When `timeout` is set, the limiter's state will be automatically removed from Redis after timeout milliseconds of inactivity. Note: timeout is 300000 (5 minutes) by default when using a Group. | ||
*/ | ||
readonly timeout?: number; | ||
[propName: string]: any; | ||
@@ -91,5 +96,2 @@ }; | ||
}; | ||
type GroupOptions = { | ||
readonly timeout?: number; | ||
}; | ||
type Callback<T> = (err: any, result: T) => void; | ||
@@ -150,3 +152,3 @@ interface ClientsList { client?: any; subscriber?: any } | ||
*/ | ||
updateSettings(options: Bottleneck.GroupOptions): void; | ||
updateSettings(options: Bottleneck.ConstructorOptions): void; | ||
@@ -153,0 +155,0 @@ /** |
@@ -585,3 +585,4 @@ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
Promise: Promise, | ||
_groupTimeout: null | ||
timeout: null, | ||
_groupConnection: null | ||
}; | ||
@@ -608,3 +609,3 @@ | ||
}).call(undefined); | ||
},{"../package.json":13,"./BottleneckError":2,"./DLList":3,"./Events":4,"./Group":5,"./Local":6,"./RedisStorage":7,"./States":8,"./Sync":9,"./parser":12}],2:[function(require,module,exports){ | ||
},{"../package.json":15,"./BottleneckError":2,"./DLList":3,"./Events":4,"./Group":5,"./Local":6,"./RedisStorage":8,"./States":10,"./Sync":11,"./parser":14}],2:[function(require,module,exports){ | ||
"use strict"; | ||
@@ -771,3 +772,3 @@ | ||
(function () { | ||
var Events, Group, parser; | ||
var Events, Group, RedisConnection, parser; | ||
@@ -778,5 +779,8 @@ parser = require("./parser"); | ||
RedisConnection = require("./RedisConnection"); | ||
Group = function () { | ||
class Group { | ||
constructor(limiterOptions = {}, groupOptions = {}) { | ||
constructor(limiterOptions = {}) { | ||
var ref, ref1; | ||
this.key = this.key.bind(this); | ||
@@ -789,3 +793,3 @@ this.deleteKey = this.deleteKey.bind(this); | ||
this.limiterOptions = limiterOptions; | ||
parser.load(groupOptions, this.defaults, this); | ||
parser.load(this.limiterOptions, this.defaults, this); | ||
this.Events = new Events(this); | ||
@@ -795,2 +799,5 @@ this.instances = {}; | ||
this._startAutoCleanup(); | ||
if (this.limiterOptions.datastore === "redis") { | ||
this._connection = new RedisConnection((ref = this.limiterOptions.clientOptions) != null ? ref : {}, (ref1 = this.limiterOptions.Promise) != null ? ref1 : Promise, this.Events); | ||
} | ||
} | ||
@@ -804,3 +811,4 @@ | ||
id: `group-key-${key}`, | ||
_groupTimeout: this.timeout | ||
timeout: this.timeout, | ||
_groupConnection: this._connection | ||
})); | ||
@@ -867,2 +875,3 @@ this.Events.trigger("created", [limiter, key]); | ||
parser.overwrite(options, this.defaults, this); | ||
parser.overwrite(options, options, this.limiterOptions); | ||
if (options.timeout != null) { | ||
@@ -873,2 +882,7 @@ return this._startAutoCleanup(); | ||
disconnect(flush) { | ||
var ref; | ||
return (ref = this._connection) != null ? ref.disconnect(flush) : void 0; | ||
} | ||
}; | ||
@@ -885,3 +899,3 @@ | ||
}).call(undefined); | ||
},{"./Bottleneck":1,"./Events":4,"./parser":12}],6:[function(require,module,exports){ | ||
},{"./Bottleneck":1,"./Events":4,"./RedisConnection":7,"./parser":14}],6:[function(require,module,exports){ | ||
"use strict"; | ||
@@ -951,3 +965,3 @@ | ||
yield _this3.yieldLoop(); | ||
return _this3._nextRequest + _this3._groupTimeout < time; | ||
return _this3._nextRequest + _this3.timeout < time; | ||
})(); | ||
@@ -1081,105 +1095,26 @@ } | ||
}).call(undefined); | ||
},{"./BottleneckError":2,"./DLList":3,"./parser":12}],7:[function(require,module,exports){ | ||
},{"./BottleneckError":2,"./DLList":3,"./parser":14}],7:[function(require,module,exports){ | ||
"use strict"; | ||
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
// Generated by CoffeeScript 2.2.4 | ||
(function () { | ||
var BottleneckError, DLList, RedisStorage, libraries, lua, parser, scriptTemplates; | ||
var RedisConnection; | ||
parser = require("./parser"); | ||
DLList = require("./DLList"); | ||
BottleneckError = require("./BottleneckError"); | ||
lua = require("./lua.json"); | ||
libraries = { | ||
get_time: lua["get_time.lua"], | ||
refresh_running: lua["refresh_running.lua"], | ||
conditions_check: lua["conditions_check.lua"], | ||
refresh_expiration: lua["refresh_expiration.lua"], | ||
validate_keys: lua["validate_keys.lua"] | ||
}; | ||
scriptTemplates = function scriptTemplates(id) { | ||
return { | ||
init: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["refresh_expiration"], | ||
code: lua["init.lua"] | ||
}, | ||
update_settings: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_expiration"], | ||
code: lua["update_settings.lua"] | ||
}, | ||
running: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running"], | ||
code: lua["running.lua"] | ||
}, | ||
group_check: { | ||
keys: [`b_${id}_settings`], | ||
libs: [], | ||
code: lua["group_check.lua"] | ||
}, | ||
check: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running", "conditions_check"], | ||
code: lua["check.lua"] | ||
}, | ||
submit: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running", "conditions_check", "refresh_expiration"], | ||
code: lua["submit.lua"] | ||
}, | ||
register: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running", "conditions_check", "refresh_expiration"], | ||
code: lua["register.lua"] | ||
}, | ||
free: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running"], | ||
code: lua["free.lua"] | ||
}, | ||
current_reservoir: { | ||
keys: [`b_${id}_settings`], | ||
libs: ["validate_keys"], | ||
code: lua["current_reservoir.lua"] | ||
}, | ||
increment_reservoir: { | ||
keys: [`b_${id}_settings`], | ||
libs: ["validate_keys"], | ||
code: lua["increment_reservoir.lua"] | ||
} | ||
}; | ||
}; | ||
RedisStorage = class RedisStorage { | ||
constructor(instance, initSettings, options) { | ||
RedisConnection = class RedisConnection { | ||
constructor(clientOptions, Promise, Events) { | ||
var redis; | ||
this.loadAll = this.loadAll.bind(this); | ||
this.instance = instance; | ||
this.initSettings = initSettings; | ||
this.clientOptions = clientOptions; | ||
this.Promise = Promise; | ||
this.Events = Events; | ||
redis = eval("require")("redis"); // Obfuscated or else Webpack/Angular will try to inline the optional redis module | ||
this.originalId = this.instance.id; | ||
this.scripts = scriptTemplates(this.originalId); | ||
parser.load(options, options, this); | ||
this.client = redis.createClient(this.clientOptions); | ||
this.subClient = redis.createClient(this.clientOptions); | ||
this.shas = {}; | ||
this.clients = { | ||
client: this.client, | ||
subscriber: this.subClient | ||
}; | ||
this.isReady = false; | ||
this.pubsubs = {}; | ||
this.loaded = false; | ||
this.ready = new this.Promise((resolve, reject) => { | ||
var count, done, errorListener; | ||
errorListener = function errorListener(e) { | ||
errorListener = e => { | ||
[this.client, this.subClient].forEach(client => { | ||
return client.removeListener("error", errorListener); | ||
}); | ||
return reject(e); | ||
@@ -1194,6 +1129,9 @@ }; | ||
return client.on("error", e => { | ||
return this.instance.Events.trigger("error", [e]); | ||
return this.Events.trigger("error", [e]); | ||
}); | ||
}); | ||
return resolve(); | ||
return resolve({ | ||
client: this.client, | ||
subscriber: this.subClient | ||
}); | ||
} | ||
@@ -1206,11 +1144,78 @@ }; | ||
this.subClient.on("error", errorListener); | ||
return this.subClient.on("ready", () => { | ||
this.subClient.on("subscribe", function () { | ||
this.subClient.on("ready", () => { | ||
this.subClient.on("psubscribe", function () { | ||
return done(); | ||
}); | ||
return this.subClient.subscribe(`b_${this.originalId}`); | ||
return this.subClient.psubscribe("bottleneck_*"); | ||
}); | ||
}).then(this.loadAll).then(() => { | ||
return this.subClient.on("pmessage", (pattern, channel, message) => { | ||
var base; | ||
return typeof (base = this.pubsubs)[channel] === "function" ? base[channel](message) : void 0; | ||
}); | ||
}); | ||
} | ||
addLimiter(instance, pubsub) { | ||
return this.pubsubs[`bottleneck_${instance.id}`] = pubsub; | ||
} | ||
removeLimiter(instance) { | ||
return delete this.pubsubs[`bottleneck_${instance.id}`]; | ||
} | ||
disconnect(flush) { | ||
this.client.end(flush); | ||
return this.subClient.end(flush); | ||
} | ||
}; | ||
module.exports = RedisConnection; | ||
}).call(undefined); | ||
},{}],8:[function(require,module,exports){ | ||
"use strict"; | ||
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
// Generated by CoffeeScript 2.2.4 | ||
(function () { | ||
var BottleneckError, RedisConnection, RedisStorage, Scripts, parser; | ||
parser = require("./parser"); | ||
BottleneckError = require("./BottleneckError"); | ||
RedisConnection = require("./RedisConnection"); | ||
Scripts = require("./Scripts"); | ||
RedisStorage = class RedisStorage { | ||
constructor(instance, initSettings, options) { | ||
var ref; | ||
this.instance = instance; | ||
this.initSettings = initSettings; | ||
this.originalId = this.instance.id; | ||
parser.load(options, options, this); | ||
this.shas = {}; | ||
this.isReady = false; | ||
this.connection = (ref = this._groupConnection) != null ? ref : new RedisConnection(this.clientOptions, this.Promise, this.instance.Events); | ||
this.ready = this.connection.ready.then(clients => { | ||
this.clients = clients; | ||
if (this.connection.loaded) { | ||
return this.Promise.resolve(); | ||
} else { | ||
return this.Promise.all(Scripts.names.map(k => { | ||
return this._loadScript(k); | ||
})); | ||
} | ||
}).then(() => { | ||
var args; | ||
this.subClient.on("message", (channel, message) => { | ||
this.connection.loaded = true; | ||
args = this.prepareInitSettings(options.clearDatastore); | ||
this.isReady = true; | ||
return this.runScript("init", args); | ||
}).then(() => { | ||
this.connection.addLimiter(this.instance, message => { | ||
var info, type; | ||
@@ -1229,6 +1234,2 @@ | ||
}); | ||
args = this.prepareInitSettings(options.clearDatastore); | ||
this.isReady = true; | ||
return this.runScript("init", args); | ||
}).then(results => { | ||
return this.clients; | ||
@@ -1239,14 +1240,13 @@ }); | ||
disconnect(flush) { | ||
this.client.end(flush); | ||
this.subClient.end(flush); | ||
return this; | ||
this.connection.removeLimiter(this.instance); | ||
if (this._groupConnection == null) { | ||
return this.connection.disconnect(flush); | ||
} | ||
} | ||
loadScript(name) { | ||
_loadScript(name) { | ||
return new this.Promise((resolve, reject) => { | ||
var payload; | ||
payload = this.scripts[name].libs.map(function (lib) { | ||
return libraries[lib]; | ||
}).join("\n") + this.scripts[name].code; | ||
return this.client.multi([["script", "load", payload]]).exec((err, replies) => { | ||
payload = Scripts.payload(name); | ||
return this.clients.client.multi([["script", "load", payload]]).exec((err, replies) => { | ||
if (err != null) { | ||
@@ -1261,14 +1261,28 @@ return reject(err); | ||
loadAll() { | ||
var k, v; | ||
return this.Promise.all(function () { | ||
var ref, results1; | ||
ref = this.scripts; | ||
results1 = []; | ||
for (k in ref) { | ||
v = ref[k]; | ||
results1.push(this.loadScript(k)); | ||
} | ||
return results1; | ||
}.call(this)); | ||
runScript(name, args) { | ||
var keys; | ||
if (!this.isReady) { | ||
return this.Promise.reject(new BottleneckError("This limiter is not done connecting to Redis yet. Wait for the '.ready()' promise to resolve before submitting requests.")); | ||
} else { | ||
keys = Scripts.keys(name, this.originalId); | ||
return new this.Promise((resolve, reject) => { | ||
var arr; | ||
arr = [this.shas[name], keys.length].concat(keys, args, function (err, replies) { | ||
if (err != null) { | ||
return reject(err); | ||
} | ||
return resolve(replies); | ||
}); | ||
this.instance.Events.trigger("debug", [`Calling Redis script: ${name}.lua`, args]); | ||
return this.clients.client.evalsha.bind(this.clients.client).apply({}, arr); | ||
}).catch(e => { | ||
if (e.message === "SETTINGS_KEY_NOT_FOUND") { | ||
return this.runScript("init", this.prepareInitSettings(false)).then(() => { | ||
return this.runScript(name, args); | ||
}); | ||
} else { | ||
return this.Promise.reject(e); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -1304,3 +1318,3 @@ | ||
version: this.instance.version, | ||
groupTimeout: this._groupTimeout | ||
groupTimeout: this.timeout | ||
})); | ||
@@ -1311,30 +1325,2 @@ args.unshift(clear ? 1 : 0); | ||
runScript(name, args) { | ||
var script; | ||
if (!this.isReady) { | ||
return this.Promise.reject(new BottleneckError("This limiter is not done connecting to Redis yet. Wait for the '.ready()' promise to resolve before submitting requests.")); | ||
} else { | ||
script = this.scripts[name]; | ||
return new this.Promise((resolve, reject) => { | ||
var arr; | ||
arr = [this.shas[name], script.keys.length].concat(script.keys, args, function (err, replies) { | ||
if (err != null) { | ||
return reject(err); | ||
} | ||
return resolve(replies); | ||
}); | ||
this.instance.Events.trigger("debug", [`Calling Redis script: ${name}.lua`, args]); | ||
return this.client.evalsha.bind(this.client).apply({}, arr); | ||
}).catch(e => { | ||
if (e.message === "SETTINGS_KEY_NOT_FOUND") { | ||
return this.runScript("init", this.prepareInitSettings(false)).then(() => { | ||
return this.runScript(name, args); | ||
}); | ||
} else { | ||
return this.Promise.reject(e); | ||
} | ||
}); | ||
} | ||
} | ||
convertBool(b) { | ||
@@ -1468,3 +1454,3 @@ return !!b; | ||
}).call(undefined); | ||
},{"./BottleneckError":2,"./DLList":3,"./lua.json":11,"./parser":12}],8:[function(require,module,exports){ | ||
},{"./BottleneckError":2,"./RedisConnection":7,"./Scripts":9,"./parser":14}],9:[function(require,module,exports){ | ||
"use strict"; | ||
@@ -1474,2 +1460,104 @@ | ||
(function () { | ||
var libraries, lua, templates; | ||
lua = require("./lua.json"); | ||
libraries = { | ||
get_time: lua["get_time.lua"], | ||
refresh_running: lua["refresh_running.lua"], | ||
conditions_check: lua["conditions_check.lua"], | ||
refresh_expiration: lua["refresh_expiration.lua"], | ||
validate_keys: lua["validate_keys.lua"] | ||
}; | ||
templates = { | ||
init: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["refresh_expiration"], | ||
code: lua["init.lua"] | ||
}, | ||
update_settings: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["validate_keys", "refresh_expiration"], | ||
code: lua["update_settings.lua"] | ||
}, | ||
running: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["validate_keys", "refresh_running"], | ||
code: lua["running.lua"] | ||
}, | ||
group_check: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`]; | ||
}, | ||
libs: [], | ||
code: lua["group_check.lua"] | ||
}, | ||
check: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["validate_keys", "refresh_running", "conditions_check"], | ||
code: lua["check.lua"] | ||
}, | ||
submit: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["validate_keys", "refresh_running", "conditions_check", "refresh_expiration"], | ||
code: lua["submit.lua"] | ||
}, | ||
register: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["validate_keys", "refresh_running", "conditions_check", "refresh_expiration"], | ||
code: lua["register.lua"] | ||
}, | ||
free: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`]; | ||
}, | ||
libs: ["validate_keys", "refresh_running"], | ||
code: lua["free.lua"] | ||
}, | ||
current_reservoir: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`]; | ||
}, | ||
libs: ["validate_keys"], | ||
code: lua["current_reservoir.lua"] | ||
}, | ||
increment_reservoir: { | ||
keys: function keys(id) { | ||
return [`b_${id}_settings`]; | ||
}, | ||
libs: ["validate_keys"], | ||
code: lua["increment_reservoir.lua"] | ||
} | ||
}; | ||
exports.names = Object.keys(templates); | ||
exports.keys = function (name, id) { | ||
return templates[name].keys(id); | ||
}; | ||
exports.payload = function (name) { | ||
return templates[name].libs.map(function (lib) { | ||
return libraries[lib]; | ||
}).join("\n") + templates[name].code; | ||
}; | ||
}).call(undefined); | ||
},{"./lua.json":13}],10:[function(require,module,exports){ | ||
"use strict"; | ||
// Generated by CoffeeScript 2.2.4 | ||
(function () { | ||
var BottleneckError, States; | ||
@@ -1532,3 +1620,3 @@ | ||
}).call(undefined); | ||
},{"./BottleneckError":2}],9:[function(require,module,exports){ | ||
},{"./BottleneckError":2}],11:[function(require,module,exports){ | ||
"use strict"; | ||
@@ -1608,3 +1696,3 @@ | ||
}).call(undefined); | ||
},{"./DLList":3}],10:[function(require,module,exports){ | ||
},{"./DLList":3}],12:[function(require,module,exports){ | ||
"use strict"; | ||
@@ -1616,3 +1704,3 @@ | ||
}).call(undefined); | ||
},{"./Bottleneck":1}],11:[function(require,module,exports){ | ||
},{"./Bottleneck":1}],13:[function(require,module,exports){ | ||
module.exports={ | ||
@@ -1628,6 +1716,6 @@ "check.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal weight = tonumber(ARGV[1])\nlocal now = tonumber(ARGV[2])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\n\nlocal conditionsCheck = conditions_check(weight, maxConcurrent, running, reservoir)\n\nlocal result = conditionsCheck and nextRequest - now <= 0\n\nreturn result\n", | ||
"refresh_expiration.lua": "local refresh_expiration = function (executing_key, running_key, settings_key, now, nextRequest, groupTimeout)\n\n if groupTimeout ~= nil then\n local ttl = (nextRequest + groupTimeout) - now\n\n redis.call('pexpire', executing_key, ttl)\n redis.call('pexpire', running_key, ttl)\n redis.call('pexpire', settings_key, ttl)\n end\n\nend\n", | ||
"refresh_running.lua": "local refresh_running = function (executing_key, running_key, settings_key, now)\n\n local expired = redis.call('zrangebyscore', executing_key, '-inf', '('..now)\n\n if #expired == 0 then\n return redis.call('hget', settings_key, 'running')\n else\n redis.call('zremrangebyscore', executing_key, '-inf', '('..now)\n\n local args = {'hmget', running_key}\n for i = 1, #expired do\n table.insert(args, expired[i])\n end\n\n local weights = redis.call(unpack(args))\n\n args[1] = 'hdel'\n local deleted = redis.call(unpack(args))\n\n local total = 0\n for i = 1, #weights do\n total = total + (tonumber(weights[i]) or 0)\n end\n local incr = -total\n if total == 0 then\n incr = 0\n else\n local id = redis.call('hget', settings_key, 'id')\n redis.call('publish', 'b_'..id, 'freed:'..total)\n end\n\n return redis.call('hincrby', settings_key, 'running', incr)\n end\n\nend\n", | ||
"refresh_running.lua": "local refresh_running = function (executing_key, running_key, settings_key, now)\n\n local expired = redis.call('zrangebyscore', executing_key, '-inf', '('..now)\n\n if #expired == 0 then\n return redis.call('hget', settings_key, 'running')\n else\n redis.call('zremrangebyscore', executing_key, '-inf', '('..now)\n\n local args = {'hmget', running_key}\n for i = 1, #expired do\n table.insert(args, expired[i])\n end\n\n local weights = redis.call(unpack(args))\n\n args[1] = 'hdel'\n local deleted = redis.call(unpack(args))\n\n local total = 0\n for i = 1, #weights do\n total = total + (tonumber(weights[i]) or 0)\n end\n local incr = -total\n if total == 0 then\n incr = 0\n else\n local id = redis.call('hget', settings_key, 'id')\n redis.call('publish', 'bottleneck_'..id, 'freed:'..total)\n end\n\n return redis.call('hincrby', settings_key, 'running', incr)\n end\n\nend\n", | ||
"register.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal index = ARGV[1]\nlocal weight = tonumber(ARGV[2])\nlocal expiration = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\nlocal minTime = tonumber(settings[4])\nlocal groupTimeout = tonumber(settings[5])\n\nif conditions_check(weight, maxConcurrent, running, reservoir) then\n\n if expiration ~= nil then\n redis.call('zadd', executing_key, now + expiration, index)\n end\n redis.call('hset', running_key, index, weight)\n redis.call('hincrby', settings_key, 'running', weight)\n\n local wait = math.max(nextRequest - now, 0)\n local newNextRequest = now + wait + minTime\n\n if reservoir == nil then\n redis.call('hset', settings_key,\n 'nextRequest', newNextRequest\n )\n else\n reservoir = reservoir - weight\n redis.call('hmset', settings_key,\n 'reservoir', reservoir,\n 'nextRequest', newNextRequest\n )\n end\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\n\n return {true, wait, reservoir}\n\nelse\n return {false}\nend\n", | ||
"running.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\nlocal now = ARGV[1]\n\nreturn tonumber(refresh_running(executing_key, running_key, settings_key, now))\n", | ||
"submit.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal queueLength = tonumber(ARGV[1])\nlocal weight = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'highWater',\n 'reservoir',\n 'nextRequest',\n 'strategy',\n 'unblockTime',\n 'penalty',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal highWater = tonumber(settings[2])\nlocal reservoir = tonumber(settings[3])\nlocal nextRequest = tonumber(settings[4])\nlocal strategy = tonumber(settings[5])\nlocal unblockTime = tonumber(settings[6])\nlocal penalty = tonumber(settings[7])\nlocal minTime = tonumber(settings[8])\nlocal groupTimeout = tonumber(settings[9])\n\nif maxConcurrent ~= nil and weight > maxConcurrent then\n return redis.error_reply('OVERWEIGHT:'..weight..':'..maxConcurrent)\nend\n\nlocal reachedHWM = (highWater ~= nil and queueLength == highWater\n and not (\n conditions_check(weight, maxConcurrent, running, reservoir)\n and nextRequest - now <= 0\n )\n)\n\nlocal blocked = strategy == 3 and (reachedHWM or unblockTime >= now)\n\nif blocked then\n local computedPenalty = penalty\n if computedPenalty == nil then\n if minTime == 0 then\n computedPenalty = 5000\n else\n computedPenalty = 15 * minTime\n end\n end\n\n local newNextRequest = unblockTime + minTime\n\n redis.call('hmset', settings_key,\n 'unblockTime', now + computedPenalty,\n 'nextRequest', newNextRequest\n )\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\nend\n\nreturn {reachedHWM, blocked, strategy}\n", | ||
"submit.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal queueLength = tonumber(ARGV[1])\nlocal weight = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'highWater',\n 'reservoir',\n 'nextRequest',\n 'strategy',\n 'unblockTime',\n 'penalty',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal highWater = tonumber(settings[2])\nlocal reservoir = tonumber(settings[3])\nlocal nextRequest = tonumber(settings[4])\nlocal strategy = tonumber(settings[5])\nlocal unblockTime = tonumber(settings[6])\nlocal penalty = tonumber(settings[7])\nlocal minTime = tonumber(settings[8])\nlocal groupTimeout = tonumber(settings[9])\n\nif maxConcurrent ~= nil and weight > maxConcurrent then\n return redis.error_reply('OVERWEIGHT:'..weight..':'..maxConcurrent)\nend\n\nlocal reachedHWM = (highWater ~= nil and queueLength == highWater\n and not (\n conditions_check(weight, maxConcurrent, running, reservoir)\n and nextRequest - now <= 0\n )\n)\n\nlocal blocked = strategy == 3 and (reachedHWM or unblockTime >= now)\n\nif blocked then\n local computedPenalty = penalty\n if computedPenalty == nil then\n if minTime == 0 then\n computedPenalty = 5000\n else\n computedPenalty = 15 * minTime\n end\n end\n\n local newNextRequest = now + computedPenalty + minTime\n\n redis.call('hmset', settings_key,\n 'unblockTime', now + computedPenalty,\n 'nextRequest', newNextRequest\n )\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\nend\n\nreturn {reachedHWM, blocked, strategy}\n", | ||
"update_settings.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal args = {'hmset', settings_key}\n\nfor i = 1, #ARGV do\n table.insert(args, ARGV[i])\nend\n\nredis.call(unpack(args))\n\nlocal groupTimeout = tonumber(redis.call('hget', settings_key, 'groupTimeout'))\nrefresh_expiration(executing_key, running_key, settings_key, 0, 0, groupTimeout)\n\nreturn {}\n", | ||
@@ -1637,3 +1725,3 @@ "validate_keys.lua": "local settings_key = KEYS[1]\n\nif not (redis.call('exists', settings_key) == 1) then\n return redis.error_reply('SETTINGS_KEY_NOT_FOUND')\nend\n" | ||
},{}],12:[function(require,module,exports){ | ||
},{}],14:[function(require,module,exports){ | ||
"use strict"; | ||
@@ -1663,6 +1751,6 @@ | ||
}).call(undefined); | ||
},{}],13:[function(require,module,exports){ | ||
},{}],15:[function(require,module,exports){ | ||
module.exports={ | ||
"name": "bottleneck", | ||
"version": "2.6.0", | ||
"version": "2.7.0", | ||
"description": "Distributed task scheduler and rate limiter", | ||
@@ -1715,2 +1803,2 @@ "main": "lib/index.js", | ||
},{}]},{},[10]); | ||
},{}]},{},[12]); |
@@ -1,1 +0,1 @@ | ||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){"use strict";var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"])_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError("Invalid attempt to destructure non-iterable instance")}}}();function _toArray(arr){return Array.isArray(arr)?arr:Array.from(arr)}function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var Bottleneck,DEFAULT_PRIORITY,DLList,Events,Local,NUM_PRIORITIES,RedisStorage,States,Sync,packagejson,parser,splice=[].splice;NUM_PRIORITIES=10;DEFAULT_PRIORITY=5;parser=require("./parser");Local=require("./Local");RedisStorage=require("./RedisStorage");Events=require("./Events");States=require("./States");DLList=require("./DLList");Sync=require("./Sync");packagejson=require("../package.json");Bottleneck=function(){class Bottleneck{constructor(options={},...invalid){var sDefaults;this.ready=this.ready.bind(this);this.clients=this.clients.bind(this);this.disconnect=this.disconnect.bind(this);this.chain=this.chain.bind(this);this.queued=this.queued.bind(this);this.running=this.running.bind(this);this.check=this.check.bind(this);this._drainOne=this._drainOne.bind(this);this.submit=this.submit.bind(this);this.schedule=this.schedule.bind(this);this.wrap=this.wrap.bind(this);this.updateSettings=this.updateSettings.bind(this);this.currentReservoir=this.currentReservoir.bind(this);this.incrementReservoir=this.incrementReservoir.bind(this);if(!(options!=null&&typeof options==="object"&&invalid.length===0)){throw new Bottleneck.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1.")}parser.load(options,this.instanceDefaults,this);this._queues=this._makeQueues();this._scheduled={};this._states=new States(["RECEIVED","QUEUED","RUNNING","EXECUTING"].concat(this.trackDoneStatus?["DONE"]:[]));this._limiter=null;this.Events=new Events(this);this._submitLock=new Sync("submit");this._registerLock=new Sync("register");sDefaults=parser.load(options,this.storeDefaults,{});this._store=function(){if(this.datastore==="local"){return new Local(parser.load(options,this.storeInstanceDefaults,sDefaults))}else if(this.datastore==="redis"){return new RedisStorage(this,sDefaults,parser.load(options,this.storeInstanceDefaults,{}))}else{throw new Bottleneck.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`)}}.call(this)}ready(){return this._store.ready}clients(){return this._store.clients}disconnect(flush=true){var _this=this;return _asyncToGenerator(function*(){return yield _this._store.disconnect(flush)})()}chain(_limiter){this._limiter=_limiter;return this}queued(priority){if(priority!=null){return this._queues[priority].length}else{return this._queues.reduce(function(a,b){return a+b.length},0)}}empty(){return this.queued()===0&&this._submitLock.isEmpty()}running(){var _this2=this;return _asyncToGenerator(function*(){return yield _this2._store.__running__()})()}jobStatus(id){return this._states.jobStatus(id)}counts(){return this._states.statusCounts()}_makeQueues(){var i,j,ref,results;results=[];for(i=j=1,ref=NUM_PRIORITIES;1<=ref?j<=ref:j>=ref;i=1<=ref?++j:--j){results.push(new DLList)}return results}_sanitizePriority(priority){var sProperty;sProperty=~~priority!==priority?DEFAULT_PRIORITY:priority;if(sProperty<0){return 0}else if(sProperty>NUM_PRIORITIES-1){return NUM_PRIORITIES-1}else{return sProperty}}_find(arr,fn){var ref;return(ref=function(){var i,j,len,x;for(i=j=0,len=arr.length;j<len;i=++j){x=arr[i];if(fn(x)){return x}}}())!=null?ref:[]}_getFirst(arr){return this._find(arr,function(x){return x.length>0})}_randomIndex(){return Math.random().toString(36).slice(2)}check(weight=1){var _this3=this;return _asyncToGenerator(function*(){return yield _this3._store.__check__(weight)})()}_run(next,wait,index){var _this4=this;var completed,done;this.Events.trigger("debug",[`Scheduling ${next.options.id}`,{args:next.args,options:next.options}]);done=false;completed=(()=>{var _ref=_asyncToGenerator(function*(...args){var e,ref,running;if(!done){try{done=true;_this4._states.next(next.options.id);clearTimeout(_this4._scheduled[index].expiration);delete _this4._scheduled[index];_this4.Events.trigger("debug",[`Completed ${next.options.id}`,{args:next.args,options:next.options}]);_this4.Events.trigger("done",[`Completed ${next.options.id}`,{args:next.args,options:next.options}]);var _ref2=yield _this4._store.__free__(index,next.options.weight);running=_ref2.running;_this4.Events.trigger("debug",[`Freed ${next.options.id}`,{args:next.args,options:next.options}]);_this4._drainAll().catch(function(e){return _this4.Events.trigger("error",[e])});if(running===0&&_this4.empty()){_this4.Events.trigger("idle",[])}return(ref=next.cb)!=null?ref.apply({},args):void 0}catch(error){e=error;return _this4.Events.trigger("error",[e])}}});return function completed(){return _ref.apply(this,arguments)}})();this._states.next(next.options.id);return this._scheduled[index]={timeout:setTimeout(()=>{this.Events.trigger("debug",[`Executing ${next.options.id}`,{args:next.args,options:next.options}]);this._states.next(next.options.id);if(this._limiter!=null){return this._limiter.submit.apply(this._limiter,Array.prototype.concat(next.options,next.task,next.args,completed))}else{return next.task.apply({},next.args.concat(completed))}},wait),expiration:next.options.expiration!=null?setTimeout(()=>{return completed(new Bottleneck.prototype.BottleneckError(`This job timed out after ${next.options.expiration} ms.`))},wait+next.options.expiration):void 0,job:next}}_drainOne(freed){return this._registerLock.schedule(()=>{var args,index,options,queue;if(this.queued()===0){return this.Promise.resolve(false)}queue=this._getFirst(this._queues);var _queue$first=queue.first();options=_queue$first.options;args=_queue$first.args;if(freed!=null&&options.weight>freed){return this.Promise.resolve(false)}this.Events.trigger("debug",[`Draining ${options.id}`,{args:args,options:options}]);index=this._randomIndex();return this._store.__register__(index,options.weight,options.expiration).then(({success:success,wait:wait,reservoir:reservoir})=>{var empty,next;this.Events.trigger("debug",[`Drained ${options.id}`,{success:success,args:args,options:options}]);if(success){next=queue.shift();empty=this.empty();if(empty){this.Events.trigger("empty",[])}if(reservoir===0){this.Events.trigger("depleted",[empty])}this._run(next,wait,index)}return this.Promise.resolve(success)})})}_drainAll(freed){return this._drainOne(freed).then(success=>{if(success){return this._drainAll()}else{return this.Promise.resolve(success)}}).catch(e=>{return this.Events.trigger("error",[e])})}_drop(job,message="This job has been dropped by Bottleneck"){var ref;this._states.remove(job.options.id);if(this.rejectOnDrop){if((ref=job.cb)!=null){ref.apply({},[new Bottleneck.prototype.BottleneckError(message)])}}return this.Events.trigger("dropped",[job])}stop(options={}){var done,waitForExecuting;options=parser.load(options,this.stopDefaults);waitForExecuting=(at=>{var finished;finished=(()=>{var counts;counts=this._states.counts;return counts[0]+counts[1]+counts[2]+counts[3]===at});return new this.Promise((resolve,reject)=>{if(finished()){return resolve()}else{return this.on("done",()=>{if(finished()){this.removeAllListeners("done");return resolve()}})}})});done=options.dropWaitingJobs?(this._run=(next=>{return this._drop(next,options.dropErrorMessage)}),this._drainOne=(()=>{return Promise.resolve(false)}),this.Promise.all([this._registerLock.schedule(()=>{return Promise.resolve(true)},this._submitLock.schedule(()=>{return Promise.resolve(true)}))]).then(()=>{var k,ref,v;ref=this._scheduled;for(k in ref){v=ref[k];if(this.jobStatus(v.job.options.id)==="RUNNING"){clearTimeout(v.timeout);clearTimeout(v.expiration);this._drop(v.job,options.dropErrorMessage)}}this._queues.forEach(queue=>{return queue.forEachShift(job=>{return this._drop(job,options.dropErrorMessage)})});return waitForExecuting(0)})):this.schedule({priority:NUM_PRIORITIES-1,weight:0},()=>{return waitForExecuting(1)});this.submit=((...args)=>{var _ref3,_ref4,_splice$call,_splice$call2;var cb,ref;ref=args,_ref3=ref,_ref4=_toArray(_ref3),args=_ref4.slice(0),_ref3,_splice$call=splice.call(args,-1),_splice$call2=_slicedToArray(_splice$call,1),cb=_splice$call2[0],_splice$call;return cb!=null?cb.apply({},[new Bottleneck.prototype.BottleneckError(options.enqueueErrorMessage)]):void 0});return done}submit(...args){var _this5=this;var cb,job,options,ref,ref1,ref2,task;if(typeof args[0]==="function"){var _ref5,_ref6,_splice$call3,_splice$call4;ref=args,_ref5=ref,_ref6=_toArray(_ref5),task=_ref6[0],args=_ref6.slice(1),_ref5,_splice$call3=splice.call(args,-1),_splice$call4=_slicedToArray(_splice$call3,1),cb=_splice$call4[0],_splice$call3;options=parser.load({},this.jobDefaults,{})}else{var _ref7,_ref8,_splice$call5,_splice$call6;ref1=args,_ref7=ref1,_ref8=_toArray(_ref7),options=_ref8[0],task=_ref8[1],args=_ref8.slice(2),_ref7,_splice$call5=splice.call(args,-1),_splice$call6=_slicedToArray(_splice$call5,1),cb=_splice$call6[0],_splice$call5;options=parser.load(options,this.jobDefaults)}job={options:options,task:task,args:args,cb:cb};options.priority=this._sanitizePriority(options.priority);if(options.id===this.jobDefaults.id){options.id=`${options.id}-${this._randomIndex()}`}if(this.jobStatus(options.id)!=null){if((ref2=job.cb)!=null){ref2.apply({},[new Bottleneck.prototype.BottleneckError(`A job with the same id already exists (id=${options.id})`)])}return false}this._states.start(options.id);this.Events.trigger("debug",[`Queueing ${options.id}`,{args:args,options:options}]);return this._submitLock.schedule(_asyncToGenerator(function*(){var blocked,e,reachedHWM,ref3,shifted,strategy;try{var _ref10=yield _this5._store.__submit__(_this5.queued(),options.weight);reachedHWM=_ref10.reachedHWM;blocked=_ref10.blocked;strategy=_ref10.strategy;_this5.Events.trigger("debug",[`Queued ${options.id}`,{args:args,options:options,reachedHWM:reachedHWM,blocked:blocked}])}catch(error){e=error;_this5._states.remove(options.id);_this5.Events.trigger("debug",[`Could not queue ${options.id}`,{args:args,options:options,error:e}]);if((ref3=job.cb)!=null){ref3.apply({},[e])}return false}if(blocked){_this5._queues=_this5._makeQueues();_this5._drop(job);return true}else if(reachedHWM){shifted=strategy===Bottleneck.prototype.strategy.LEAK?_this5._getFirst(_this5._queues.slice(options.priority).reverse()).shift():strategy===Bottleneck.prototype.strategy.OVERFLOW_PRIORITY?_this5._getFirst(_this5._queues.slice(options.priority+1).reverse()).shift():strategy===Bottleneck.prototype.strategy.OVERFLOW?job:void 0;if(shifted!=null){_this5._drop(shifted)}if(shifted==null||strategy===Bottleneck.prototype.strategy.OVERFLOW){if(shifted==null){_this5._drop(job)}return reachedHWM}}_this5._states.next(job.options.id);_this5._queues[options.priority].push(job);yield _this5._drainAll();return reachedHWM}))}schedule(...args){var options,task,wrapped;if(typeof args[0]==="function"){var _args=args;var _args2=_toArray(_args);task=_args2[0];args=_args2.slice(1);options=parser.load({},this.jobDefaults,{})}else{var _args3=args;var _args4=_toArray(_args3);options=_args4[0];task=_args4[1];args=_args4.slice(2);options=parser.load(options,this.jobDefaults)}wrapped=function wrapped(...args){var _ref11,_ref12,_splice$call7,_splice$call8;var cb,ref,returned;ref=args,_ref11=ref,_ref12=_toArray(_ref11),args=_ref12.slice(0),_ref11,_splice$call7=splice.call(args,-1),_splice$call8=_slicedToArray(_splice$call7,1),cb=_splice$call8[0],_splice$call7;returned=task.apply({},args);return(!((returned!=null?returned.then:void 0)!=null&&typeof returned.then==="function")?Promise.resolve(returned):returned).then(function(...args){return cb.apply({},Array.prototype.concat(null,args))}).catch(function(...args){return cb.apply({},args)})};return new this.Promise((resolve,reject)=>{return this.submit.apply({},Array.prototype.concat(options,wrapped,args,function(...args){return(args[0]!=null?reject:(args.shift(),resolve)).apply({},args)})).catch(e=>{return this.Events.trigger("error",[e])})})}wrap(fn){var ret;ret=((...args)=>{return this.schedule.apply({},Array.prototype.concat(fn,args))});ret.withOptions=((options,...args)=>{return this.schedule.apply({},Array.prototype.concat(options,fn,args))});return ret}updateSettings(options={}){var _this6=this;return _asyncToGenerator(function*(){yield _this6._store.__updateSettings__(parser.overwrite(options,_this6.storeDefaults));parser.overwrite(options,_this6.instanceDefaults,_this6);_this6._drainAll().catch(function(e){return _this6.Events.trigger("error",[e])});return _this6})()}currentReservoir(){var _this7=this;return _asyncToGenerator(function*(){return yield _this7._store.__currentReservoir__()})()}incrementReservoir(incr=0){var _this8=this;return _asyncToGenerator(function*(){yield _this8._store.__incrementReservoir__(incr);_this8._drainAll().catch(function(e){return _this8.Events.trigger("error",[e])});return _this8})()}}Bottleneck.default=Bottleneck;Bottleneck.version=Bottleneck.prototype.version=packagejson.version;Bottleneck.strategy=Bottleneck.prototype.strategy={LEAK:1,OVERFLOW:2,OVERFLOW_PRIORITY:4,BLOCK:3};Bottleneck.BottleneckError=Bottleneck.prototype.BottleneckError=require("./BottleneckError");Bottleneck.Group=Bottleneck.prototype.Group=require("./Group");Bottleneck.prototype.jobDefaults={priority:DEFAULT_PRIORITY,weight:1,expiration:null,id:"<no-id>"};Bottleneck.prototype.storeDefaults={maxConcurrent:null,minTime:0,highWater:null,strategy:Bottleneck.prototype.strategy.LEAK,penalty:null,reservoir:null};Bottleneck.prototype.storeInstanceDefaults={clientOptions:{},clearDatastore:false,Promise:Promise,_groupTimeout:null};Bottleneck.prototype.instanceDefaults={datastore:"local",id:"<no-id>",rejectOnDrop:true,trackDoneStatus:false,Promise:Promise};Bottleneck.prototype.stopDefaults={enqueueErrorMessage:"This limiter has been stopped and cannot accept new jobs.",dropWaitingJobs:true,dropErrorMessage:"This limiter has been stopped."};return Bottleneck}.call(this);module.exports=Bottleneck}).call(undefined)},{"../package.json":13,"./BottleneckError":2,"./DLList":3,"./Events":4,"./Group":5,"./Local":6,"./RedisStorage":7,"./States":8,"./Sync":9,"./parser":12}],2:[function(require,module,exports){"use strict";(function(){var BottleneckError;BottleneckError=class BottleneckError extends Error{};module.exports=BottleneckError}).call(undefined)},{}],3:[function(require,module,exports){"use strict";(function(){var DLList;DLList=class DLList{constructor(){this._first=null;this._last=null;this.length=0}push(value){var node;this.length++;node={value:value,next:null};if(this._last!=null){this._last.next=node;this._last=node}else{this._first=this._last=node}return void 0}shift(){var ref1,value;if(this._first==null){return void 0}else{this.length--}value=this._first.value;this._first=(ref1=this._first.next)!=null?ref1:this._last=null;return value}first(){if(this._first!=null){return this._first.value}}getArray(){var node,ref,results;node=this._first;results=[];while(node!=null){results.push((ref=node,node=node.next,ref.value))}return results}forEachShift(cb){var node;node=this.shift();while(node!=null){cb(node),node=this.shift()}return void 0}};module.exports=DLList}).call(undefined)},{}],4:[function(require,module,exports){"use strict";(function(){var Events;Events=class Events{constructor(instance){this.instance=instance;this._events={};this.instance.on=((name,cb)=>{return this._addListener(name,"many",cb)});this.instance.once=((name,cb)=>{return this._addListener(name,"once",cb)});this.instance.removeAllListeners=((name=null)=>{if(name!=null){return delete this._events[name]}else{return this._events={}}})}_addListener(name,status,cb){var base;if((base=this._events)[name]==null){base[name]=[]}this._events[name].push({cb:cb,status:status});return this.instance}trigger(name,args){if(name!=="debug"){this.trigger("debug",[`Event triggered: ${name}`,args])}if(this._events[name]==null){return}this._events[name]=this._events[name].filter(function(listener){return listener.status!=="none"});return this._events[name].forEach(listener=>{var e,ret;if(listener.status==="none"){return}if(listener.status==="once"){listener.status="none"}try{ret=listener.cb.apply({},args);if(typeof(ret!=null?ret.then:void 0)==="function"){return ret.then(function(){}).catch(e=>{return this.trigger("error",[e])})}}catch(error){e=error;if("name"!=="error"){return this.trigger("error",[e])}}})}};module.exports=Events}).call(undefined)},{}],5:[function(require,module,exports){"use strict";function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var Events,Group,parser;parser=require("./parser");Events=require("./Events");Group=function(){class Group{constructor(limiterOptions={},groupOptions={}){this.key=this.key.bind(this);this.deleteKey=this.deleteKey.bind(this);this.limiters=this.limiters.bind(this);this.keys=this.keys.bind(this);this._startAutoCleanup=this._startAutoCleanup.bind(this);this.updateSettings=this.updateSettings.bind(this);this.limiterOptions=limiterOptions;parser.load(groupOptions,this.defaults,this);this.Events=new Events(this);this.instances={};this.Bottleneck=require("./Bottleneck");this._startAutoCleanup()}key(key=""){var ref;return(ref=this.instances[key])!=null?ref:(()=>{var limiter;limiter=this.instances[key]=new this.Bottleneck(Object.assign(this.limiterOptions,{id:`group-key-${key}`,_groupTimeout:this.timeout}));this.Events.trigger("created",[limiter,key]);return limiter})()}deleteKey(key=""){var ref;if((ref=this.instances[key])!=null){ref.disconnect()}return delete this.instances[key]}limiters(){var k,ref,results,v;ref=this.instances;results=[];for(k in ref){v=ref[k];results.push({key:k,limiter:v})}return results}keys(){return Object.keys(this.instances)}_startAutoCleanup(){var _this=this;var base;clearInterval(this.interval);return typeof(base=this.interval=setInterval(_asyncToGenerator(function*(){var e,k,ref,results,time,v;time=Date.now();ref=_this.instances;results=[];for(k in ref){v=ref[k];try{if(yield v._store.__groupCheck__(time)){results.push(_this.deleteKey(k))}else{results.push(void 0)}}catch(error){e=error;results.push(v.Events.trigger("error",[e]))}}return results}),this.timeout/2)).unref==="function"?base.unref():void 0}updateSettings(options={}){parser.overwrite(options,this.defaults,this);if(options.timeout!=null){return this._startAutoCleanup()}}}Group.prototype.defaults={timeout:1e3*60*5};return Group}.call(this);module.exports=Group}).call(undefined)},{"./Bottleneck":1,"./Events":4,"./parser":12}],6:[function(require,module,exports){"use strict";function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var BottleneckError,DLList,Local,parser;parser=require("./parser");DLList=require("./DLList");BottleneckError=require("./BottleneckError");Local=class Local{constructor(options){parser.load(options,options,this);this._nextRequest=Date.now();this._running=0;this._executing={};this._unblockTime=0;this.ready=this.yieldLoop();this.clients={}}disconnect(flush){return this}yieldLoop(t=0){return new this.Promise(function(resolve,reject){return setTimeout(resolve,t)})}computePenalty(){var ref;return(ref=this.penalty)!=null?ref:15*this.minTime||5e3}__updateSettings__(options){var _this=this;return _asyncToGenerator(function*(){yield _this.yieldLoop();parser.overwrite(options,options,_this);return true})()}__running__(){var _this2=this;return _asyncToGenerator(function*(){yield _this2.yieldLoop();return _this2._running})()}__groupCheck__(time){var _this3=this;return _asyncToGenerator(function*(){yield _this3.yieldLoop();return _this3._nextRequest+_this3._groupTimeout<time})()}conditionsCheck(weight){return(this.maxConcurrent==null||this._running+weight<=this.maxConcurrent)&&(this.reservoir==null||this.reservoir-weight>=0)}__incrementReservoir__(incr){var _this4=this;return _asyncToGenerator(function*(){yield _this4.yieldLoop();return _this4.reservoir+=incr})()}__currentReservoir__(){var _this5=this;return _asyncToGenerator(function*(){yield _this5.yieldLoop();return _this5.reservoir})()}isBlocked(now){return this._unblockTime>=now}check(weight,now){return this.conditionsCheck(weight)&&this._nextRequest-now<=0}__check__(weight){var _this6=this;return _asyncToGenerator(function*(){var now;yield _this6.yieldLoop();now=Date.now();return _this6.check(weight,now)})()}__register__(index,weight,expiration){var _this7=this;return _asyncToGenerator(function*(){var now,wait;yield _this7.yieldLoop();now=Date.now();if(_this7.conditionsCheck(weight)){_this7._running+=weight;_this7._executing[index]={timeout:expiration!=null?setTimeout(function(){if(!_this7._executing[index].freed){_this7._executing[index].freed=true;return _this7._running-=weight}},expiration):void 0,freed:false};if(_this7.reservoir!=null){_this7.reservoir-=weight}wait=Math.max(_this7._nextRequest-now,0);_this7._nextRequest=now+wait+_this7.minTime;return{success:true,wait:wait,reservoir:_this7.reservoir}}else{return{success:false}}})()}strategyIsBlock(){return this.strategy===3}__submit__(queueLength,weight){var _this8=this;return _asyncToGenerator(function*(){var blocked,now,reachedHWM;yield _this8.yieldLoop();if(_this8.maxConcurrent!=null&&weight>_this8.maxConcurrent){throw new BottleneckError(`Impossible to add a job having a weight of ${weight} to a limiter having a maxConcurrent setting of ${_this8.maxConcurrent}`)}now=Date.now();reachedHWM=_this8.highWater!=null&&queueLength===_this8.highWater&&!_this8.check(weight,now);blocked=_this8.strategyIsBlock()&&(reachedHWM||_this8.isBlocked(now));if(blocked){_this8._unblockTime=now+_this8.computePenalty();_this8._nextRequest=_this8._unblockTime+_this8.minTime}return{reachedHWM:reachedHWM,blocked:blocked,strategy:_this8.strategy}})()}__free__(index,weight){var _this9=this;return _asyncToGenerator(function*(){yield _this9.yieldLoop();clearTimeout(_this9._executing[index].timeout);if(!_this9._executing[index].freed){_this9._executing[index].freed=true;_this9._running-=weight}return{running:_this9._running}})()}};module.exports=Local}).call(undefined)},{"./BottleneckError":2,"./DLList":3,"./parser":12}],7:[function(require,module,exports){"use strict";var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"])_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError("Invalid attempt to destructure non-iterable instance")}}}();function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var BottleneckError,DLList,RedisStorage,libraries,lua,parser,scriptTemplates;parser=require("./parser");DLList=require("./DLList");BottleneckError=require("./BottleneckError");lua=require("./lua.json");libraries={get_time:lua["get_time.lua"],refresh_running:lua["refresh_running.lua"],conditions_check:lua["conditions_check.lua"],refresh_expiration:lua["refresh_expiration.lua"],validate_keys:lua["validate_keys.lua"]};scriptTemplates=function scriptTemplates(id){return{init:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["refresh_expiration"],code:lua["init.lua"]},update_settings:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["validate_keys","refresh_expiration"],code:lua["update_settings.lua"]},running:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["validate_keys","refresh_running"],code:lua["running.lua"]},group_check:{keys:[`b_${id}_settings`],libs:[],code:lua["group_check.lua"]},check:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["validate_keys","refresh_running","conditions_check"],code:lua["check.lua"]},submit:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["validate_keys","refresh_running","conditions_check","refresh_expiration"],code:lua["submit.lua"]},register:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["validate_keys","refresh_running","conditions_check","refresh_expiration"],code:lua["register.lua"]},free:{keys:[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`],libs:["validate_keys","refresh_running"],code:lua["free.lua"]},current_reservoir:{keys:[`b_${id}_settings`],libs:["validate_keys"],code:lua["current_reservoir.lua"]},increment_reservoir:{keys:[`b_${id}_settings`],libs:["validate_keys"],code:lua["increment_reservoir.lua"]}}};RedisStorage=class RedisStorage{constructor(instance,initSettings,options){var redis;this.loadAll=this.loadAll.bind(this);this.instance=instance;this.initSettings=initSettings;redis=eval("require")("redis");this.originalId=this.instance.id;this.scripts=scriptTemplates(this.originalId);parser.load(options,options,this);this.client=redis.createClient(this.clientOptions);this.subClient=redis.createClient(this.clientOptions);this.shas={};this.clients={client:this.client,subscriber:this.subClient};this.isReady=false;this.ready=new this.Promise((resolve,reject)=>{var count,done,errorListener;errorListener=function errorListener(e){return reject(e)};count=0;done=(()=>{count++;if(count===2){[this.client,this.subClient].forEach(client=>{client.removeListener("error",errorListener);return client.on("error",e=>{return this.instance.Events.trigger("error",[e])})});return resolve()}});this.client.on("error",errorListener);this.client.on("ready",function(){return done()});this.subClient.on("error",errorListener);return this.subClient.on("ready",()=>{this.subClient.on("subscribe",function(){return done()});return this.subClient.subscribe(`b_${this.originalId}`)})}).then(this.loadAll).then(()=>{var args;this.subClient.on("message",(channel,message)=>{var info,type;var _message$split=message.split(":");var _message$split2=_slicedToArray(_message$split,2);type=_message$split2[0];info=_message$split2[1];if(type==="freed"){return this.instance._drainAll(~~info)}});args=this.prepareInitSettings(options.clearDatastore);this.isReady=true;return this.runScript("init",args)}).then(results=>{return this.clients})}disconnect(flush){this.client.end(flush);this.subClient.end(flush);return this}loadScript(name){return new this.Promise((resolve,reject)=>{var payload;payload=this.scripts[name].libs.map(function(lib){return libraries[lib]}).join("\n")+this.scripts[name].code;return this.client.multi([["script","load",payload]]).exec((err,replies)=>{if(err!=null){return reject(err)}this.shas[name]=replies[0];return resolve(replies[0])})})}loadAll(){var k,v;return this.Promise.all(function(){var ref,results1;ref=this.scripts;results1=[];for(k in ref){v=ref[k];results1.push(this.loadScript(k))}return results1}.call(this))}prepareArray(arr){return arr.map(function(x){if(x!=null){return x.toString()}else{return""}})}prepareObject(obj){var arr,k,v;arr=[];for(k in obj){v=obj[k];arr.push(k,v!=null?v.toString():"")}return arr}prepareInitSettings(clear){var args;args=this.prepareObject(Object.assign({},this.initSettings,{id:this.originalId,nextRequest:Date.now(),running:0,unblockTime:0,version:this.instance.version,groupTimeout:this._groupTimeout}));args.unshift(clear?1:0);return args}runScript(name,args){var script;if(!this.isReady){return this.Promise.reject(new BottleneckError("This limiter is not done connecting to Redis yet. Wait for the '.ready()' promise to resolve before submitting requests."))}else{script=this.scripts[name];return new this.Promise((resolve,reject)=>{var arr;arr=[this.shas[name],script.keys.length].concat(script.keys,args,function(err,replies){if(err!=null){return reject(err)}return resolve(replies)});this.instance.Events.trigger("debug",[`Calling Redis script: ${name}.lua`,args]);return this.client.evalsha.bind(this.client).apply({},arr)}).catch(e=>{if(e.message==="SETTINGS_KEY_NOT_FOUND"){return this.runScript("init",this.prepareInitSettings(false)).then(()=>{return this.runScript(name,args)})}else{return this.Promise.reject(e)}})}}convertBool(b){return!!b}__updateSettings__(options){var _this=this;return _asyncToGenerator(function*(){return yield _this.runScript("update_settings",_this.prepareObject(options))})()}__running__(){var _this2=this;return _asyncToGenerator(function*(){return yield _this2.runScript("running",[Date.now()])})()}__groupCheck__(){var _this3=this;return _asyncToGenerator(function*(){return _this3.convertBool(yield _this3.runScript("group_check",[]))})()}__incrementReservoir__(incr){var _this4=this;return _asyncToGenerator(function*(){return yield _this4.runScript("increment_reservoir",[incr])})()}__currentReservoir__(){var _this5=this;return _asyncToGenerator(function*(){return yield _this5.runScript("current_reservoir",[])})()}__check__(weight){var _this6=this;return _asyncToGenerator(function*(){return _this6.convertBool(yield _this6.runScript("check",_this6.prepareArray([weight,Date.now()])))})()}__register__(index,weight,expiration){var _this7=this;return _asyncToGenerator(function*(){var reservoir,success,wait;var _ref=yield _this7.runScript("register",_this7.prepareArray([index,weight,expiration,Date.now()]));var _ref2=_slicedToArray(_ref,3);success=_ref2[0];wait=_ref2[1];reservoir=_ref2[2];return{success:_this7.convertBool(success),wait:wait,reservoir:reservoir}})()}__submit__(queueLength,weight){var _this8=this;return _asyncToGenerator(function*(){var blocked,e,maxConcurrent,overweight,reachedHWM,strategy;try{var _ref3=yield _this8.runScript("submit",_this8.prepareArray([queueLength,weight,Date.now()]));var _ref4=_slicedToArray(_ref3,3);reachedHWM=_ref4[0];blocked=_ref4[1];strategy=_ref4[2];return{reachedHWM:_this8.convertBool(reachedHWM),blocked:_this8.convertBool(blocked),strategy:strategy}}catch(error){e=error;if(e.message.indexOf("OVERWEIGHT")===0){var _e$message$split=e.message.split(":");var _e$message$split2=_slicedToArray(_e$message$split,3);overweight=_e$message$split2[0];weight=_e$message$split2[1];maxConcurrent=_e$message$split2[2];throw new BottleneckError(`Impossible to add a job having a weight of ${weight} to a limiter having a maxConcurrent setting of ${maxConcurrent}`)}else{throw e}}})()}__free__(index,weight){var _this9=this;return _asyncToGenerator(function*(){var result;result=yield _this9.runScript("free",_this9.prepareArray([index,Date.now()]));return{running:result}})()}};module.exports=RedisStorage}).call(undefined)},{"./BottleneckError":2,"./DLList":3,"./lua.json":11,"./parser":12}],8:[function(require,module,exports){"use strict";(function(){var BottleneckError,States;BottleneckError=require("./BottleneckError");States=class States{constructor(status){this.status=status;this.jobs={};this.counts=this.status.map(function(){return 0})}next(id){var current,next;current=this.jobs[id];next=current+1;if(current!=null&&next<this.status.length){this.counts[current]--;this.counts[next]++;return this.jobs[id]++}else if(current!=null){this.counts[current]--;return delete this.jobs[id]}}start(id,initial=0){this.jobs[id]=initial;return this.counts[initial]++}remove(id){var current;current=this.jobs[id];if(current!=null){this.counts[current]--;return delete this.jobs[id]}}jobStatus(id){var ref;return(ref=this.status[this.jobs[id]])!=null?ref:null}statusCounts(){return this.counts.reduce((acc,v,i)=>{acc[this.status[i]]=v;return acc},{})}};module.exports=States}).call(undefined)},{"./BottleneckError":2}],9:[function(require,module,exports){"use strict";var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"])_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError("Invalid attempt to destructure non-iterable instance")}}}();function _toArray(arr){return Array.isArray(arr)?arr:Array.from(arr)}(function(){var DLList,Sync,splice=[].splice;DLList=require("./DLList");Sync=class Sync{constructor(name){this.submit=this.submit.bind(this);this.schedule=this.schedule.bind(this);this.name=name;this._running=0;this._queue=new DLList}isEmpty(){return this._queue.length===0}_tryToRun(){var next;if(this._running<1&&this._queue.length>0){this._running++;next=this._queue.shift();return next.task.apply({},next.args.concat((...args)=>{var ref;this._running--;this._tryToRun();return(ref=next.cb)!=null?ref.apply({},args):void 0}))}}submit(task,...args){var _ref,_ref2,_splice$call,_splice$call2;var cb,ref;ref=args,_ref=ref,_ref2=_toArray(_ref),args=_ref2.slice(0),_ref,_splice$call=splice.call(args,-1),_splice$call2=_slicedToArray(_splice$call,1),cb=_splice$call2[0],_splice$call;this._queue.push({task:task,args:args,cb:cb});return this._tryToRun()}schedule(task,...args){var wrapped;wrapped=function wrapped(...args){var _ref3,_ref4,_splice$call3,_splice$call4;var cb,ref;ref=args,_ref3=ref,_ref4=_toArray(_ref3),args=_ref4.slice(0),_ref3,_splice$call3=splice.call(args,-1),_splice$call4=_slicedToArray(_splice$call3,1),cb=_splice$call4[0],_splice$call3;return task.apply({},args).then(function(...args){return cb.apply({},Array.prototype.concat(null,args))}).catch(function(...args){return cb.apply({},args)})};return new Promise((resolve,reject)=>{return this.submit.apply({},Array.prototype.concat(wrapped,args,function(...args){return(args[0]!=null?reject:(args.shift(),resolve)).apply({},args)}))})}};module.exports=Sync}).call(undefined)},{"./DLList":3}],10:[function(require,module,exports){"use strict";(function(){module.exports=require("./Bottleneck")}).call(undefined)},{"./Bottleneck":1}],11:[function(require,module,exports){module.exports={"check.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal weight = tonumber(ARGV[1])\nlocal now = tonumber(ARGV[2])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\n\nlocal conditionsCheck = conditions_check(weight, maxConcurrent, running, reservoir)\n\nlocal result = conditionsCheck and nextRequest - now <= 0\n\nreturn result\n","conditions_check.lua":"local conditions_check = function (weight, maxConcurrent, running, reservoir)\n return (\n (maxConcurrent == nil or running + weight <= maxConcurrent) and\n (reservoir == nil or reservoir - weight >= 0)\n )\nend\n","current_reservoir.lua":"local settings_key = KEYS[1]\n\nreturn tonumber(redis.call('hget', settings_key, 'reservoir'))\n","free.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal index = ARGV[1]\nlocal now = ARGV[2]\n\nredis.call('zadd', executing_key, 0, index)\n\nreturn refresh_running(executing_key, running_key, settings_key, now)\n","get_time.lua":"redis.replicate_commands()\n\nlocal get_time = function ()\n local time = redis.call('time')\n\n return tonumber(time[1]..string.sub(time[2], 1, 3))\nend\n","group_check.lua":"local settings_key = KEYS[1]\n\nreturn not (redis.call('exists', settings_key) == 1)\n","increment_reservoir.lua":"local settings_key = KEYS[1]\nlocal incr = ARGV[1]\n\nreturn redis.call('hincrby', settings_key, 'reservoir', incr)\n","init.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal clear = tonumber(ARGV[1])\n\nif clear == 1 then\n redis.call('del', settings_key, running_key, executing_key)\nend\n\nif redis.call('exists', settings_key) == 0 then\n local args = {'hmset', settings_key}\n\n for i = 2, #ARGV do\n table.insert(args, ARGV[i])\n end\n\n redis.call(unpack(args))\nend\n\nlocal groupTimeout = tonumber(redis.call('hget', settings_key, 'groupTimeout'))\nrefresh_expiration(executing_key, running_key, settings_key, 0, 0, groupTimeout)\n\nreturn {}\n","refresh_expiration.lua":"local refresh_expiration = function (executing_key, running_key, settings_key, now, nextRequest, groupTimeout)\n\n if groupTimeout ~= nil then\n local ttl = (nextRequest + groupTimeout) - now\n\n redis.call('pexpire', executing_key, ttl)\n redis.call('pexpire', running_key, ttl)\n redis.call('pexpire', settings_key, ttl)\n end\n\nend\n","refresh_running.lua":"local refresh_running = function (executing_key, running_key, settings_key, now)\n\n local expired = redis.call('zrangebyscore', executing_key, '-inf', '('..now)\n\n if #expired == 0 then\n return redis.call('hget', settings_key, 'running')\n else\n redis.call('zremrangebyscore', executing_key, '-inf', '('..now)\n\n local args = {'hmget', running_key}\n for i = 1, #expired do\n table.insert(args, expired[i])\n end\n\n local weights = redis.call(unpack(args))\n\n args[1] = 'hdel'\n local deleted = redis.call(unpack(args))\n\n local total = 0\n for i = 1, #weights do\n total = total + (tonumber(weights[i]) or 0)\n end\n local incr = -total\n if total == 0 then\n incr = 0\n else\n local id = redis.call('hget', settings_key, 'id')\n redis.call('publish', 'b_'..id, 'freed:'..total)\n end\n\n return redis.call('hincrby', settings_key, 'running', incr)\n end\n\nend\n","register.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal index = ARGV[1]\nlocal weight = tonumber(ARGV[2])\nlocal expiration = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\nlocal minTime = tonumber(settings[4])\nlocal groupTimeout = tonumber(settings[5])\n\nif conditions_check(weight, maxConcurrent, running, reservoir) then\n\n if expiration ~= nil then\n redis.call('zadd', executing_key, now + expiration, index)\n end\n redis.call('hset', running_key, index, weight)\n redis.call('hincrby', settings_key, 'running', weight)\n\n local wait = math.max(nextRequest - now, 0)\n local newNextRequest = now + wait + minTime\n\n if reservoir == nil then\n redis.call('hset', settings_key,\n 'nextRequest', newNextRequest\n )\n else\n reservoir = reservoir - weight\n redis.call('hmset', settings_key,\n 'reservoir', reservoir,\n 'nextRequest', newNextRequest\n )\n end\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\n\n return {true, wait, reservoir}\n\nelse\n return {false}\nend\n","running.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\nlocal now = ARGV[1]\n\nreturn tonumber(refresh_running(executing_key, running_key, settings_key, now))\n","submit.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal queueLength = tonumber(ARGV[1])\nlocal weight = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'highWater',\n 'reservoir',\n 'nextRequest',\n 'strategy',\n 'unblockTime',\n 'penalty',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal highWater = tonumber(settings[2])\nlocal reservoir = tonumber(settings[3])\nlocal nextRequest = tonumber(settings[4])\nlocal strategy = tonumber(settings[5])\nlocal unblockTime = tonumber(settings[6])\nlocal penalty = tonumber(settings[7])\nlocal minTime = tonumber(settings[8])\nlocal groupTimeout = tonumber(settings[9])\n\nif maxConcurrent ~= nil and weight > maxConcurrent then\n return redis.error_reply('OVERWEIGHT:'..weight..':'..maxConcurrent)\nend\n\nlocal reachedHWM = (highWater ~= nil and queueLength == highWater\n and not (\n conditions_check(weight, maxConcurrent, running, reservoir)\n and nextRequest - now <= 0\n )\n)\n\nlocal blocked = strategy == 3 and (reachedHWM or unblockTime >= now)\n\nif blocked then\n local computedPenalty = penalty\n if computedPenalty == nil then\n if minTime == 0 then\n computedPenalty = 5000\n else\n computedPenalty = 15 * minTime\n end\n end\n\n local newNextRequest = unblockTime + minTime\n\n redis.call('hmset', settings_key,\n 'unblockTime', now + computedPenalty,\n 'nextRequest', newNextRequest\n )\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\nend\n\nreturn {reachedHWM, blocked, strategy}\n","update_settings.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal args = {'hmset', settings_key}\n\nfor i = 1, #ARGV do\n table.insert(args, ARGV[i])\nend\n\nredis.call(unpack(args))\n\nlocal groupTimeout = tonumber(redis.call('hget', settings_key, 'groupTimeout'))\nrefresh_expiration(executing_key, running_key, settings_key, 0, 0, groupTimeout)\n\nreturn {}\n","validate_keys.lua":"local settings_key = KEYS[1]\n\nif not (redis.call('exists', settings_key) == 1) then\n return redis.error_reply('SETTINGS_KEY_NOT_FOUND')\nend\n"}},{}],12:[function(require,module,exports){"use strict";(function(){exports.load=function(received,defaults,onto={}){var k,ref,v;for(k in defaults){v=defaults[k];onto[k]=(ref=received[k])!=null?ref:v}return onto};exports.overwrite=function(received,defaults,onto={}){var k,v;for(k in received){v=received[k];if(defaults[k]!==void 0){onto[k]=v}}return onto}}).call(undefined)},{}],13:[function(require,module,exports){module.exports={name:"bottleneck",version:"2.6.0",description:"Distributed task scheduler and rate limiter",main:"lib/index.js",typings:"bottleneck.d.ts",scripts:{test:"./node_modules/mocha/bin/mocha test",build:"./scripts/build.sh",compile:"./scripts/build.sh compile"},repository:{type:"git",url:"https://github.com/SGrondin/bottleneck"},keywords:["async rate limiter","rate limiter","rate limiting","async","rate","limiting","limiter","throttle","throttling","load","ddos"],author:{name:"Simon Grondin"},license:"MIT",bugs:{url:"https://github.com/SGrondin/bottleneck/issues"},devDependencies:{"@types/es6-promise":"0.0.33",assert:"1.4.x","babel-core":"^6.26.0","babel-preset-env":"^1.6.1",browserify:"*",coffeescript:"2.2.x","ejs-cli":"2.0.1",mocha:"4.x",redis:"^2.8.0",typescript:"^2.6.2","uglify-es":"3.x"}}},{}]},{},[10]); | ||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){"use strict";var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"])_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError("Invalid attempt to destructure non-iterable instance")}}}();function _toArray(arr){return Array.isArray(arr)?arr:Array.from(arr)}function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var Bottleneck,DEFAULT_PRIORITY,DLList,Events,Local,NUM_PRIORITIES,RedisStorage,States,Sync,packagejson,parser,splice=[].splice;NUM_PRIORITIES=10;DEFAULT_PRIORITY=5;parser=require("./parser");Local=require("./Local");RedisStorage=require("./RedisStorage");Events=require("./Events");States=require("./States");DLList=require("./DLList");Sync=require("./Sync");packagejson=require("../package.json");Bottleneck=function(){class Bottleneck{constructor(options={},...invalid){var sDefaults;this.ready=this.ready.bind(this);this.clients=this.clients.bind(this);this.disconnect=this.disconnect.bind(this);this.chain=this.chain.bind(this);this.queued=this.queued.bind(this);this.running=this.running.bind(this);this.check=this.check.bind(this);this._drainOne=this._drainOne.bind(this);this.submit=this.submit.bind(this);this.schedule=this.schedule.bind(this);this.wrap=this.wrap.bind(this);this.updateSettings=this.updateSettings.bind(this);this.currentReservoir=this.currentReservoir.bind(this);this.incrementReservoir=this.incrementReservoir.bind(this);if(!(options!=null&&typeof options==="object"&&invalid.length===0)){throw new Bottleneck.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1.")}parser.load(options,this.instanceDefaults,this);this._queues=this._makeQueues();this._scheduled={};this._states=new States(["RECEIVED","QUEUED","RUNNING","EXECUTING"].concat(this.trackDoneStatus?["DONE"]:[]));this._limiter=null;this.Events=new Events(this);this._submitLock=new Sync("submit");this._registerLock=new Sync("register");sDefaults=parser.load(options,this.storeDefaults,{});this._store=function(){if(this.datastore==="local"){return new Local(parser.load(options,this.storeInstanceDefaults,sDefaults))}else if(this.datastore==="redis"){return new RedisStorage(this,sDefaults,parser.load(options,this.storeInstanceDefaults,{}))}else{throw new Bottleneck.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`)}}.call(this)}ready(){return this._store.ready}clients(){return this._store.clients}disconnect(flush=true){var _this=this;return _asyncToGenerator(function*(){return yield _this._store.disconnect(flush)})()}chain(_limiter){this._limiter=_limiter;return this}queued(priority){if(priority!=null){return this._queues[priority].length}else{return this._queues.reduce(function(a,b){return a+b.length},0)}}empty(){return this.queued()===0&&this._submitLock.isEmpty()}running(){var _this2=this;return _asyncToGenerator(function*(){return yield _this2._store.__running__()})()}jobStatus(id){return this._states.jobStatus(id)}counts(){return this._states.statusCounts()}_makeQueues(){var i,j,ref,results;results=[];for(i=j=1,ref=NUM_PRIORITIES;1<=ref?j<=ref:j>=ref;i=1<=ref?++j:--j){results.push(new DLList)}return results}_sanitizePriority(priority){var sProperty;sProperty=~~priority!==priority?DEFAULT_PRIORITY:priority;if(sProperty<0){return 0}else if(sProperty>NUM_PRIORITIES-1){return NUM_PRIORITIES-1}else{return sProperty}}_find(arr,fn){var ref;return(ref=function(){var i,j,len,x;for(i=j=0,len=arr.length;j<len;i=++j){x=arr[i];if(fn(x)){return x}}}())!=null?ref:[]}_getFirst(arr){return this._find(arr,function(x){return x.length>0})}_randomIndex(){return Math.random().toString(36).slice(2)}check(weight=1){var _this3=this;return _asyncToGenerator(function*(){return yield _this3._store.__check__(weight)})()}_run(next,wait,index){var _this4=this;var completed,done;this.Events.trigger("debug",[`Scheduling ${next.options.id}`,{args:next.args,options:next.options}]);done=false;completed=(()=>{var _ref=_asyncToGenerator(function*(...args){var e,ref,running;if(!done){try{done=true;_this4._states.next(next.options.id);clearTimeout(_this4._scheduled[index].expiration);delete _this4._scheduled[index];_this4.Events.trigger("debug",[`Completed ${next.options.id}`,{args:next.args,options:next.options}]);_this4.Events.trigger("done",[`Completed ${next.options.id}`,{args:next.args,options:next.options}]);var _ref2=yield _this4._store.__free__(index,next.options.weight);running=_ref2.running;_this4.Events.trigger("debug",[`Freed ${next.options.id}`,{args:next.args,options:next.options}]);_this4._drainAll().catch(function(e){return _this4.Events.trigger("error",[e])});if(running===0&&_this4.empty()){_this4.Events.trigger("idle",[])}return(ref=next.cb)!=null?ref.apply({},args):void 0}catch(error){e=error;return _this4.Events.trigger("error",[e])}}});return function completed(){return _ref.apply(this,arguments)}})();this._states.next(next.options.id);return this._scheduled[index]={timeout:setTimeout(()=>{this.Events.trigger("debug",[`Executing ${next.options.id}`,{args:next.args,options:next.options}]);this._states.next(next.options.id);if(this._limiter!=null){return this._limiter.submit.apply(this._limiter,Array.prototype.concat(next.options,next.task,next.args,completed))}else{return next.task.apply({},next.args.concat(completed))}},wait),expiration:next.options.expiration!=null?setTimeout(()=>{return completed(new Bottleneck.prototype.BottleneckError(`This job timed out after ${next.options.expiration} ms.`))},wait+next.options.expiration):void 0,job:next}}_drainOne(freed){return this._registerLock.schedule(()=>{var args,index,options,queue;if(this.queued()===0){return this.Promise.resolve(false)}queue=this._getFirst(this._queues);var _queue$first=queue.first();options=_queue$first.options;args=_queue$first.args;if(freed!=null&&options.weight>freed){return this.Promise.resolve(false)}this.Events.trigger("debug",[`Draining ${options.id}`,{args:args,options:options}]);index=this._randomIndex();return this._store.__register__(index,options.weight,options.expiration).then(({success:success,wait:wait,reservoir:reservoir})=>{var empty,next;this.Events.trigger("debug",[`Drained ${options.id}`,{success:success,args:args,options:options}]);if(success){next=queue.shift();empty=this.empty();if(empty){this.Events.trigger("empty",[])}if(reservoir===0){this.Events.trigger("depleted",[empty])}this._run(next,wait,index)}return this.Promise.resolve(success)})})}_drainAll(freed){return this._drainOne(freed).then(success=>{if(success){return this._drainAll()}else{return this.Promise.resolve(success)}}).catch(e=>{return this.Events.trigger("error",[e])})}_drop(job,message="This job has been dropped by Bottleneck"){var ref;this._states.remove(job.options.id);if(this.rejectOnDrop){if((ref=job.cb)!=null){ref.apply({},[new Bottleneck.prototype.BottleneckError(message)])}}return this.Events.trigger("dropped",[job])}stop(options={}){var done,waitForExecuting;options=parser.load(options,this.stopDefaults);waitForExecuting=(at=>{var finished;finished=(()=>{var counts;counts=this._states.counts;return counts[0]+counts[1]+counts[2]+counts[3]===at});return new this.Promise((resolve,reject)=>{if(finished()){return resolve()}else{return this.on("done",()=>{if(finished()){this.removeAllListeners("done");return resolve()}})}})});done=options.dropWaitingJobs?(this._run=(next=>{return this._drop(next,options.dropErrorMessage)}),this._drainOne=(()=>{return Promise.resolve(false)}),this.Promise.all([this._registerLock.schedule(()=>{return Promise.resolve(true)},this._submitLock.schedule(()=>{return Promise.resolve(true)}))]).then(()=>{var k,ref,v;ref=this._scheduled;for(k in ref){v=ref[k];if(this.jobStatus(v.job.options.id)==="RUNNING"){clearTimeout(v.timeout);clearTimeout(v.expiration);this._drop(v.job,options.dropErrorMessage)}}this._queues.forEach(queue=>{return queue.forEachShift(job=>{return this._drop(job,options.dropErrorMessage)})});return waitForExecuting(0)})):this.schedule({priority:NUM_PRIORITIES-1,weight:0},()=>{return waitForExecuting(1)});this.submit=((...args)=>{var _ref3,_ref4,_splice$call,_splice$call2;var cb,ref;ref=args,_ref3=ref,_ref4=_toArray(_ref3),args=_ref4.slice(0),_ref3,_splice$call=splice.call(args,-1),_splice$call2=_slicedToArray(_splice$call,1),cb=_splice$call2[0],_splice$call;return cb!=null?cb.apply({},[new Bottleneck.prototype.BottleneckError(options.enqueueErrorMessage)]):void 0});return done}submit(...args){var _this5=this;var cb,job,options,ref,ref1,ref2,task;if(typeof args[0]==="function"){var _ref5,_ref6,_splice$call3,_splice$call4;ref=args,_ref5=ref,_ref6=_toArray(_ref5),task=_ref6[0],args=_ref6.slice(1),_ref5,_splice$call3=splice.call(args,-1),_splice$call4=_slicedToArray(_splice$call3,1),cb=_splice$call4[0],_splice$call3;options=parser.load({},this.jobDefaults,{})}else{var _ref7,_ref8,_splice$call5,_splice$call6;ref1=args,_ref7=ref1,_ref8=_toArray(_ref7),options=_ref8[0],task=_ref8[1],args=_ref8.slice(2),_ref7,_splice$call5=splice.call(args,-1),_splice$call6=_slicedToArray(_splice$call5,1),cb=_splice$call6[0],_splice$call5;options=parser.load(options,this.jobDefaults)}job={options:options,task:task,args:args,cb:cb};options.priority=this._sanitizePriority(options.priority);if(options.id===this.jobDefaults.id){options.id=`${options.id}-${this._randomIndex()}`}if(this.jobStatus(options.id)!=null){if((ref2=job.cb)!=null){ref2.apply({},[new Bottleneck.prototype.BottleneckError(`A job with the same id already exists (id=${options.id})`)])}return false}this._states.start(options.id);this.Events.trigger("debug",[`Queueing ${options.id}`,{args:args,options:options}]);return this._submitLock.schedule(_asyncToGenerator(function*(){var blocked,e,reachedHWM,ref3,shifted,strategy;try{var _ref10=yield _this5._store.__submit__(_this5.queued(),options.weight);reachedHWM=_ref10.reachedHWM;blocked=_ref10.blocked;strategy=_ref10.strategy;_this5.Events.trigger("debug",[`Queued ${options.id}`,{args:args,options:options,reachedHWM:reachedHWM,blocked:blocked}])}catch(error){e=error;_this5._states.remove(options.id);_this5.Events.trigger("debug",[`Could not queue ${options.id}`,{args:args,options:options,error:e}]);if((ref3=job.cb)!=null){ref3.apply({},[e])}return false}if(blocked){_this5._queues=_this5._makeQueues();_this5._drop(job);return true}else if(reachedHWM){shifted=strategy===Bottleneck.prototype.strategy.LEAK?_this5._getFirst(_this5._queues.slice(options.priority).reverse()).shift():strategy===Bottleneck.prototype.strategy.OVERFLOW_PRIORITY?_this5._getFirst(_this5._queues.slice(options.priority+1).reverse()).shift():strategy===Bottleneck.prototype.strategy.OVERFLOW?job:void 0;if(shifted!=null){_this5._drop(shifted)}if(shifted==null||strategy===Bottleneck.prototype.strategy.OVERFLOW){if(shifted==null){_this5._drop(job)}return reachedHWM}}_this5._states.next(job.options.id);_this5._queues[options.priority].push(job);yield _this5._drainAll();return reachedHWM}))}schedule(...args){var options,task,wrapped;if(typeof args[0]==="function"){var _args=args;var _args2=_toArray(_args);task=_args2[0];args=_args2.slice(1);options=parser.load({},this.jobDefaults,{})}else{var _args3=args;var _args4=_toArray(_args3);options=_args4[0];task=_args4[1];args=_args4.slice(2);options=parser.load(options,this.jobDefaults)}wrapped=function wrapped(...args){var _ref11,_ref12,_splice$call7,_splice$call8;var cb,ref,returned;ref=args,_ref11=ref,_ref12=_toArray(_ref11),args=_ref12.slice(0),_ref11,_splice$call7=splice.call(args,-1),_splice$call8=_slicedToArray(_splice$call7,1),cb=_splice$call8[0],_splice$call7;returned=task.apply({},args);return(!((returned!=null?returned.then:void 0)!=null&&typeof returned.then==="function")?Promise.resolve(returned):returned).then(function(...args){return cb.apply({},Array.prototype.concat(null,args))}).catch(function(...args){return cb.apply({},args)})};return new this.Promise((resolve,reject)=>{return this.submit.apply({},Array.prototype.concat(options,wrapped,args,function(...args){return(args[0]!=null?reject:(args.shift(),resolve)).apply({},args)})).catch(e=>{return this.Events.trigger("error",[e])})})}wrap(fn){var ret;ret=((...args)=>{return this.schedule.apply({},Array.prototype.concat(fn,args))});ret.withOptions=((options,...args)=>{return this.schedule.apply({},Array.prototype.concat(options,fn,args))});return ret}updateSettings(options={}){var _this6=this;return _asyncToGenerator(function*(){yield _this6._store.__updateSettings__(parser.overwrite(options,_this6.storeDefaults));parser.overwrite(options,_this6.instanceDefaults,_this6);_this6._drainAll().catch(function(e){return _this6.Events.trigger("error",[e])});return _this6})()}currentReservoir(){var _this7=this;return _asyncToGenerator(function*(){return yield _this7._store.__currentReservoir__()})()}incrementReservoir(incr=0){var _this8=this;return _asyncToGenerator(function*(){yield _this8._store.__incrementReservoir__(incr);_this8._drainAll().catch(function(e){return _this8.Events.trigger("error",[e])});return _this8})()}}Bottleneck.default=Bottleneck;Bottleneck.version=Bottleneck.prototype.version=packagejson.version;Bottleneck.strategy=Bottleneck.prototype.strategy={LEAK:1,OVERFLOW:2,OVERFLOW_PRIORITY:4,BLOCK:3};Bottleneck.BottleneckError=Bottleneck.prototype.BottleneckError=require("./BottleneckError");Bottleneck.Group=Bottleneck.prototype.Group=require("./Group");Bottleneck.prototype.jobDefaults={priority:DEFAULT_PRIORITY,weight:1,expiration:null,id:"<no-id>"};Bottleneck.prototype.storeDefaults={maxConcurrent:null,minTime:0,highWater:null,strategy:Bottleneck.prototype.strategy.LEAK,penalty:null,reservoir:null};Bottleneck.prototype.storeInstanceDefaults={clientOptions:{},clearDatastore:false,Promise:Promise,timeout:null,_groupConnection:null};Bottleneck.prototype.instanceDefaults={datastore:"local",id:"<no-id>",rejectOnDrop:true,trackDoneStatus:false,Promise:Promise};Bottleneck.prototype.stopDefaults={enqueueErrorMessage:"This limiter has been stopped and cannot accept new jobs.",dropWaitingJobs:true,dropErrorMessage:"This limiter has been stopped."};return Bottleneck}.call(this);module.exports=Bottleneck}).call(undefined)},{"../package.json":15,"./BottleneckError":2,"./DLList":3,"./Events":4,"./Group":5,"./Local":6,"./RedisStorage":8,"./States":10,"./Sync":11,"./parser":14}],2:[function(require,module,exports){"use strict";(function(){var BottleneckError;BottleneckError=class BottleneckError extends Error{};module.exports=BottleneckError}).call(undefined)},{}],3:[function(require,module,exports){"use strict";(function(){var DLList;DLList=class DLList{constructor(){this._first=null;this._last=null;this.length=0}push(value){var node;this.length++;node={value:value,next:null};if(this._last!=null){this._last.next=node;this._last=node}else{this._first=this._last=node}return void 0}shift(){var ref1,value;if(this._first==null){return void 0}else{this.length--}value=this._first.value;this._first=(ref1=this._first.next)!=null?ref1:this._last=null;return value}first(){if(this._first!=null){return this._first.value}}getArray(){var node,ref,results;node=this._first;results=[];while(node!=null){results.push((ref=node,node=node.next,ref.value))}return results}forEachShift(cb){var node;node=this.shift();while(node!=null){cb(node),node=this.shift()}return void 0}};module.exports=DLList}).call(undefined)},{}],4:[function(require,module,exports){"use strict";(function(){var Events;Events=class Events{constructor(instance){this.instance=instance;this._events={};this.instance.on=((name,cb)=>{return this._addListener(name,"many",cb)});this.instance.once=((name,cb)=>{return this._addListener(name,"once",cb)});this.instance.removeAllListeners=((name=null)=>{if(name!=null){return delete this._events[name]}else{return this._events={}}})}_addListener(name,status,cb){var base;if((base=this._events)[name]==null){base[name]=[]}this._events[name].push({cb:cb,status:status});return this.instance}trigger(name,args){if(name!=="debug"){this.trigger("debug",[`Event triggered: ${name}`,args])}if(this._events[name]==null){return}this._events[name]=this._events[name].filter(function(listener){return listener.status!=="none"});return this._events[name].forEach(listener=>{var e,ret;if(listener.status==="none"){return}if(listener.status==="once"){listener.status="none"}try{ret=listener.cb.apply({},args);if(typeof(ret!=null?ret.then:void 0)==="function"){return ret.then(function(){}).catch(e=>{return this.trigger("error",[e])})}}catch(error){e=error;if("name"!=="error"){return this.trigger("error",[e])}}})}};module.exports=Events}).call(undefined)},{}],5:[function(require,module,exports){"use strict";function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var Events,Group,RedisConnection,parser;parser=require("./parser");Events=require("./Events");RedisConnection=require("./RedisConnection");Group=function(){class Group{constructor(limiterOptions={}){var ref,ref1;this.key=this.key.bind(this);this.deleteKey=this.deleteKey.bind(this);this.limiters=this.limiters.bind(this);this.keys=this.keys.bind(this);this._startAutoCleanup=this._startAutoCleanup.bind(this);this.updateSettings=this.updateSettings.bind(this);this.limiterOptions=limiterOptions;parser.load(this.limiterOptions,this.defaults,this);this.Events=new Events(this);this.instances={};this.Bottleneck=require("./Bottleneck");this._startAutoCleanup();if(this.limiterOptions.datastore==="redis"){this._connection=new RedisConnection((ref=this.limiterOptions.clientOptions)!=null?ref:{},(ref1=this.limiterOptions.Promise)!=null?ref1:Promise,this.Events)}}key(key=""){var ref;return(ref=this.instances[key])!=null?ref:(()=>{var limiter;limiter=this.instances[key]=new this.Bottleneck(Object.assign(this.limiterOptions,{id:`group-key-${key}`,timeout:this.timeout,_groupConnection:this._connection}));this.Events.trigger("created",[limiter,key]);return limiter})()}deleteKey(key=""){var ref;if((ref=this.instances[key])!=null){ref.disconnect()}return delete this.instances[key]}limiters(){var k,ref,results,v;ref=this.instances;results=[];for(k in ref){v=ref[k];results.push({key:k,limiter:v})}return results}keys(){return Object.keys(this.instances)}_startAutoCleanup(){var _this=this;var base;clearInterval(this.interval);return typeof(base=this.interval=setInterval(_asyncToGenerator(function*(){var e,k,ref,results,time,v;time=Date.now();ref=_this.instances;results=[];for(k in ref){v=ref[k];try{if(yield v._store.__groupCheck__(time)){results.push(_this.deleteKey(k))}else{results.push(void 0)}}catch(error){e=error;results.push(v.Events.trigger("error",[e]))}}return results}),this.timeout/2)).unref==="function"?base.unref():void 0}updateSettings(options={}){parser.overwrite(options,this.defaults,this);parser.overwrite(options,options,this.limiterOptions);if(options.timeout!=null){return this._startAutoCleanup()}}disconnect(flush){var ref;return(ref=this._connection)!=null?ref.disconnect(flush):void 0}}Group.prototype.defaults={timeout:1e3*60*5};return Group}.call(this);module.exports=Group}).call(undefined)},{"./Bottleneck":1,"./Events":4,"./RedisConnection":7,"./parser":14}],6:[function(require,module,exports){"use strict";function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var BottleneckError,DLList,Local,parser;parser=require("./parser");DLList=require("./DLList");BottleneckError=require("./BottleneckError");Local=class Local{constructor(options){parser.load(options,options,this);this._nextRequest=Date.now();this._running=0;this._executing={};this._unblockTime=0;this.ready=this.yieldLoop();this.clients={}}disconnect(flush){return this}yieldLoop(t=0){return new this.Promise(function(resolve,reject){return setTimeout(resolve,t)})}computePenalty(){var ref;return(ref=this.penalty)!=null?ref:15*this.minTime||5e3}__updateSettings__(options){var _this=this;return _asyncToGenerator(function*(){yield _this.yieldLoop();parser.overwrite(options,options,_this);return true})()}__running__(){var _this2=this;return _asyncToGenerator(function*(){yield _this2.yieldLoop();return _this2._running})()}__groupCheck__(time){var _this3=this;return _asyncToGenerator(function*(){yield _this3.yieldLoop();return _this3._nextRequest+_this3.timeout<time})()}conditionsCheck(weight){return(this.maxConcurrent==null||this._running+weight<=this.maxConcurrent)&&(this.reservoir==null||this.reservoir-weight>=0)}__incrementReservoir__(incr){var _this4=this;return _asyncToGenerator(function*(){yield _this4.yieldLoop();return _this4.reservoir+=incr})()}__currentReservoir__(){var _this5=this;return _asyncToGenerator(function*(){yield _this5.yieldLoop();return _this5.reservoir})()}isBlocked(now){return this._unblockTime>=now}check(weight,now){return this.conditionsCheck(weight)&&this._nextRequest-now<=0}__check__(weight){var _this6=this;return _asyncToGenerator(function*(){var now;yield _this6.yieldLoop();now=Date.now();return _this6.check(weight,now)})()}__register__(index,weight,expiration){var _this7=this;return _asyncToGenerator(function*(){var now,wait;yield _this7.yieldLoop();now=Date.now();if(_this7.conditionsCheck(weight)){_this7._running+=weight;_this7._executing[index]={timeout:expiration!=null?setTimeout(function(){if(!_this7._executing[index].freed){_this7._executing[index].freed=true;return _this7._running-=weight}},expiration):void 0,freed:false};if(_this7.reservoir!=null){_this7.reservoir-=weight}wait=Math.max(_this7._nextRequest-now,0);_this7._nextRequest=now+wait+_this7.minTime;return{success:true,wait:wait,reservoir:_this7.reservoir}}else{return{success:false}}})()}strategyIsBlock(){return this.strategy===3}__submit__(queueLength,weight){var _this8=this;return _asyncToGenerator(function*(){var blocked,now,reachedHWM;yield _this8.yieldLoop();if(_this8.maxConcurrent!=null&&weight>_this8.maxConcurrent){throw new BottleneckError(`Impossible to add a job having a weight of ${weight} to a limiter having a maxConcurrent setting of ${_this8.maxConcurrent}`)}now=Date.now();reachedHWM=_this8.highWater!=null&&queueLength===_this8.highWater&&!_this8.check(weight,now);blocked=_this8.strategyIsBlock()&&(reachedHWM||_this8.isBlocked(now));if(blocked){_this8._unblockTime=now+_this8.computePenalty();_this8._nextRequest=_this8._unblockTime+_this8.minTime}return{reachedHWM:reachedHWM,blocked:blocked,strategy:_this8.strategy}})()}__free__(index,weight){var _this9=this;return _asyncToGenerator(function*(){yield _this9.yieldLoop();clearTimeout(_this9._executing[index].timeout);if(!_this9._executing[index].freed){_this9._executing[index].freed=true;_this9._running-=weight}return{running:_this9._running}})()}};module.exports=Local}).call(undefined)},{"./BottleneckError":2,"./DLList":3,"./parser":14}],7:[function(require,module,exports){"use strict";(function(){var RedisConnection;RedisConnection=class RedisConnection{constructor(clientOptions,Promise,Events){var redis;this.clientOptions=clientOptions;this.Promise=Promise;this.Events=Events;redis=eval("require")("redis");this.client=redis.createClient(this.clientOptions);this.subClient=redis.createClient(this.clientOptions);this.pubsubs={};this.loaded=false;this.ready=new this.Promise((resolve,reject)=>{var count,done,errorListener;errorListener=(e=>{[this.client,this.subClient].forEach(client=>{return client.removeListener("error",errorListener)});return reject(e)});count=0;done=(()=>{count++;if(count===2){[this.client,this.subClient].forEach(client=>{client.removeListener("error",errorListener);return client.on("error",e=>{return this.Events.trigger("error",[e])})});return resolve({client:this.client,subscriber:this.subClient})}});this.client.on("error",errorListener);this.client.on("ready",function(){return done()});this.subClient.on("error",errorListener);this.subClient.on("ready",()=>{this.subClient.on("psubscribe",function(){return done()});return this.subClient.psubscribe("bottleneck_*")});return this.subClient.on("pmessage",(pattern,channel,message)=>{var base;return typeof(base=this.pubsubs)[channel]==="function"?base[channel](message):void 0})})}addLimiter(instance,pubsub){return this.pubsubs[`bottleneck_${instance.id}`]=pubsub}removeLimiter(instance){return delete this.pubsubs[`bottleneck_${instance.id}`]}disconnect(flush){this.client.end(flush);return this.subClient.end(flush)}};module.exports=RedisConnection}).call(undefined)},{}],8:[function(require,module,exports){"use strict";var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"])_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError("Invalid attempt to destructure non-iterable instance")}}}();function _asyncToGenerator(fn){return function(){var gen=fn.apply(this,arguments);return new Promise(function(resolve,reject){function step(key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{return Promise.resolve(value).then(function(value){step("next",value)},function(err){step("throw",err)})}}return step("next")})}}(function(){var BottleneckError,RedisConnection,RedisStorage,Scripts,parser;parser=require("./parser");BottleneckError=require("./BottleneckError");RedisConnection=require("./RedisConnection");Scripts=require("./Scripts");RedisStorage=class RedisStorage{constructor(instance,initSettings,options){var ref;this.instance=instance;this.initSettings=initSettings;this.originalId=this.instance.id;parser.load(options,options,this);this.shas={};this.isReady=false;this.connection=(ref=this._groupConnection)!=null?ref:new RedisConnection(this.clientOptions,this.Promise,this.instance.Events);this.ready=this.connection.ready.then(clients=>{this.clients=clients;if(this.connection.loaded){return this.Promise.resolve()}else{return this.Promise.all(Scripts.names.map(k=>{return this._loadScript(k)}))}}).then(()=>{var args;this.connection.loaded=true;args=this.prepareInitSettings(options.clearDatastore);this.isReady=true;return this.runScript("init",args)}).then(()=>{this.connection.addLimiter(this.instance,message=>{var info,type;var _message$split=message.split(":");var _message$split2=_slicedToArray(_message$split,2);type=_message$split2[0];info=_message$split2[1];if(type==="freed"){return this.instance._drainAll(~~info)}});return this.clients})}disconnect(flush){this.connection.removeLimiter(this.instance);if(this._groupConnection==null){return this.connection.disconnect(flush)}}_loadScript(name){return new this.Promise((resolve,reject)=>{var payload;payload=Scripts.payload(name);return this.clients.client.multi([["script","load",payload]]).exec((err,replies)=>{if(err!=null){return reject(err)}this.shas[name]=replies[0];return resolve(replies[0])})})}runScript(name,args){var keys;if(!this.isReady){return this.Promise.reject(new BottleneckError("This limiter is not done connecting to Redis yet. Wait for the '.ready()' promise to resolve before submitting requests."))}else{keys=Scripts.keys(name,this.originalId);return new this.Promise((resolve,reject)=>{var arr;arr=[this.shas[name],keys.length].concat(keys,args,function(err,replies){if(err!=null){return reject(err)}return resolve(replies)});this.instance.Events.trigger("debug",[`Calling Redis script: ${name}.lua`,args]);return this.clients.client.evalsha.bind(this.clients.client).apply({},arr)}).catch(e=>{if(e.message==="SETTINGS_KEY_NOT_FOUND"){return this.runScript("init",this.prepareInitSettings(false)).then(()=>{return this.runScript(name,args)})}else{return this.Promise.reject(e)}})}}prepareArray(arr){return arr.map(function(x){if(x!=null){return x.toString()}else{return""}})}prepareObject(obj){var arr,k,v;arr=[];for(k in obj){v=obj[k];arr.push(k,v!=null?v.toString():"")}return arr}prepareInitSettings(clear){var args;args=this.prepareObject(Object.assign({},this.initSettings,{id:this.originalId,nextRequest:Date.now(),running:0,unblockTime:0,version:this.instance.version,groupTimeout:this.timeout}));args.unshift(clear?1:0);return args}convertBool(b){return!!b}__updateSettings__(options){var _this=this;return _asyncToGenerator(function*(){return yield _this.runScript("update_settings",_this.prepareObject(options))})()}__running__(){var _this2=this;return _asyncToGenerator(function*(){return yield _this2.runScript("running",[Date.now()])})()}__groupCheck__(){var _this3=this;return _asyncToGenerator(function*(){return _this3.convertBool(yield _this3.runScript("group_check",[]))})()}__incrementReservoir__(incr){var _this4=this;return _asyncToGenerator(function*(){return yield _this4.runScript("increment_reservoir",[incr])})()}__currentReservoir__(){var _this5=this;return _asyncToGenerator(function*(){return yield _this5.runScript("current_reservoir",[])})()}__check__(weight){var _this6=this;return _asyncToGenerator(function*(){return _this6.convertBool(yield _this6.runScript("check",_this6.prepareArray([weight,Date.now()])))})()}__register__(index,weight,expiration){var _this7=this;return _asyncToGenerator(function*(){var reservoir,success,wait;var _ref=yield _this7.runScript("register",_this7.prepareArray([index,weight,expiration,Date.now()]));var _ref2=_slicedToArray(_ref,3);success=_ref2[0];wait=_ref2[1];reservoir=_ref2[2];return{success:_this7.convertBool(success),wait:wait,reservoir:reservoir}})()}__submit__(queueLength,weight){var _this8=this;return _asyncToGenerator(function*(){var blocked,e,maxConcurrent,overweight,reachedHWM,strategy;try{var _ref3=yield _this8.runScript("submit",_this8.prepareArray([queueLength,weight,Date.now()]));var _ref4=_slicedToArray(_ref3,3);reachedHWM=_ref4[0];blocked=_ref4[1];strategy=_ref4[2];return{reachedHWM:_this8.convertBool(reachedHWM),blocked:_this8.convertBool(blocked),strategy:strategy}}catch(error){e=error;if(e.message.indexOf("OVERWEIGHT")===0){var _e$message$split=e.message.split(":");var _e$message$split2=_slicedToArray(_e$message$split,3);overweight=_e$message$split2[0];weight=_e$message$split2[1];maxConcurrent=_e$message$split2[2];throw new BottleneckError(`Impossible to add a job having a weight of ${weight} to a limiter having a maxConcurrent setting of ${maxConcurrent}`)}else{throw e}}})()}__free__(index,weight){var _this9=this;return _asyncToGenerator(function*(){var result;result=yield _this9.runScript("free",_this9.prepareArray([index,Date.now()]));return{running:result}})()}};module.exports=RedisStorage}).call(undefined)},{"./BottleneckError":2,"./RedisConnection":7,"./Scripts":9,"./parser":14}],9:[function(require,module,exports){"use strict";(function(){var libraries,lua,templates;lua=require("./lua.json");libraries={get_time:lua["get_time.lua"],refresh_running:lua["refresh_running.lua"],conditions_check:lua["conditions_check.lua"],refresh_expiration:lua["refresh_expiration.lua"],validate_keys:lua["validate_keys.lua"]};templates={init:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["refresh_expiration"],code:lua["init.lua"]},update_settings:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["validate_keys","refresh_expiration"],code:lua["update_settings.lua"]},running:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["validate_keys","refresh_running"],code:lua["running.lua"]},group_check:{keys:function keys(id){return[`b_${id}_settings`]},libs:[],code:lua["group_check.lua"]},check:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["validate_keys","refresh_running","conditions_check"],code:lua["check.lua"]},submit:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["validate_keys","refresh_running","conditions_check","refresh_expiration"],code:lua["submit.lua"]},register:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["validate_keys","refresh_running","conditions_check","refresh_expiration"],code:lua["register.lua"]},free:{keys:function keys(id){return[`b_${id}_settings`,`b_${id}_running`,`b_${id}_executing`]},libs:["validate_keys","refresh_running"],code:lua["free.lua"]},current_reservoir:{keys:function keys(id){return[`b_${id}_settings`]},libs:["validate_keys"],code:lua["current_reservoir.lua"]},increment_reservoir:{keys:function keys(id){return[`b_${id}_settings`]},libs:["validate_keys"],code:lua["increment_reservoir.lua"]}};exports.names=Object.keys(templates);exports.keys=function(name,id){return templates[name].keys(id)};exports.payload=function(name){return templates[name].libs.map(function(lib){return libraries[lib]}).join("\n")+templates[name].code}}).call(undefined)},{"./lua.json":13}],10:[function(require,module,exports){"use strict";(function(){var BottleneckError,States;BottleneckError=require("./BottleneckError");States=class States{constructor(status){this.status=status;this.jobs={};this.counts=this.status.map(function(){return 0})}next(id){var current,next;current=this.jobs[id];next=current+1;if(current!=null&&next<this.status.length){this.counts[current]--;this.counts[next]++;return this.jobs[id]++}else if(current!=null){this.counts[current]--;return delete this.jobs[id]}}start(id,initial=0){this.jobs[id]=initial;return this.counts[initial]++}remove(id){var current;current=this.jobs[id];if(current!=null){this.counts[current]--;return delete this.jobs[id]}}jobStatus(id){var ref;return(ref=this.status[this.jobs[id]])!=null?ref:null}statusCounts(){return this.counts.reduce((acc,v,i)=>{acc[this.status[i]]=v;return acc},{})}};module.exports=States}).call(undefined)},{"./BottleneckError":2}],11:[function(require,module,exports){"use strict";var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"])_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError("Invalid attempt to destructure non-iterable instance")}}}();function _toArray(arr){return Array.isArray(arr)?arr:Array.from(arr)}(function(){var DLList,Sync,splice=[].splice;DLList=require("./DLList");Sync=class Sync{constructor(name){this.submit=this.submit.bind(this);this.schedule=this.schedule.bind(this);this.name=name;this._running=0;this._queue=new DLList}isEmpty(){return this._queue.length===0}_tryToRun(){var next;if(this._running<1&&this._queue.length>0){this._running++;next=this._queue.shift();return next.task.apply({},next.args.concat((...args)=>{var ref;this._running--;this._tryToRun();return(ref=next.cb)!=null?ref.apply({},args):void 0}))}}submit(task,...args){var _ref,_ref2,_splice$call,_splice$call2;var cb,ref;ref=args,_ref=ref,_ref2=_toArray(_ref),args=_ref2.slice(0),_ref,_splice$call=splice.call(args,-1),_splice$call2=_slicedToArray(_splice$call,1),cb=_splice$call2[0],_splice$call;this._queue.push({task:task,args:args,cb:cb});return this._tryToRun()}schedule(task,...args){var wrapped;wrapped=function wrapped(...args){var _ref3,_ref4,_splice$call3,_splice$call4;var cb,ref;ref=args,_ref3=ref,_ref4=_toArray(_ref3),args=_ref4.slice(0),_ref3,_splice$call3=splice.call(args,-1),_splice$call4=_slicedToArray(_splice$call3,1),cb=_splice$call4[0],_splice$call3;return task.apply({},args).then(function(...args){return cb.apply({},Array.prototype.concat(null,args))}).catch(function(...args){return cb.apply({},args)})};return new Promise((resolve,reject)=>{return this.submit.apply({},Array.prototype.concat(wrapped,args,function(...args){return(args[0]!=null?reject:(args.shift(),resolve)).apply({},args)}))})}};module.exports=Sync}).call(undefined)},{"./DLList":3}],12:[function(require,module,exports){"use strict";(function(){module.exports=require("./Bottleneck")}).call(undefined)},{"./Bottleneck":1}],13:[function(require,module,exports){module.exports={"check.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal weight = tonumber(ARGV[1])\nlocal now = tonumber(ARGV[2])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\n\nlocal conditionsCheck = conditions_check(weight, maxConcurrent, running, reservoir)\n\nlocal result = conditionsCheck and nextRequest - now <= 0\n\nreturn result\n","conditions_check.lua":"local conditions_check = function (weight, maxConcurrent, running, reservoir)\n return (\n (maxConcurrent == nil or running + weight <= maxConcurrent) and\n (reservoir == nil or reservoir - weight >= 0)\n )\nend\n","current_reservoir.lua":"local settings_key = KEYS[1]\n\nreturn tonumber(redis.call('hget', settings_key, 'reservoir'))\n","free.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal index = ARGV[1]\nlocal now = ARGV[2]\n\nredis.call('zadd', executing_key, 0, index)\n\nreturn refresh_running(executing_key, running_key, settings_key, now)\n","get_time.lua":"redis.replicate_commands()\n\nlocal get_time = function ()\n local time = redis.call('time')\n\n return tonumber(time[1]..string.sub(time[2], 1, 3))\nend\n","group_check.lua":"local settings_key = KEYS[1]\n\nreturn not (redis.call('exists', settings_key) == 1)\n","increment_reservoir.lua":"local settings_key = KEYS[1]\nlocal incr = ARGV[1]\n\nreturn redis.call('hincrby', settings_key, 'reservoir', incr)\n","init.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal clear = tonumber(ARGV[1])\n\nif clear == 1 then\n redis.call('del', settings_key, running_key, executing_key)\nend\n\nif redis.call('exists', settings_key) == 0 then\n local args = {'hmset', settings_key}\n\n for i = 2, #ARGV do\n table.insert(args, ARGV[i])\n end\n\n redis.call(unpack(args))\nend\n\nlocal groupTimeout = tonumber(redis.call('hget', settings_key, 'groupTimeout'))\nrefresh_expiration(executing_key, running_key, settings_key, 0, 0, groupTimeout)\n\nreturn {}\n","refresh_expiration.lua":"local refresh_expiration = function (executing_key, running_key, settings_key, now, nextRequest, groupTimeout)\n\n if groupTimeout ~= nil then\n local ttl = (nextRequest + groupTimeout) - now\n\n redis.call('pexpire', executing_key, ttl)\n redis.call('pexpire', running_key, ttl)\n redis.call('pexpire', settings_key, ttl)\n end\n\nend\n","refresh_running.lua":"local refresh_running = function (executing_key, running_key, settings_key, now)\n\n local expired = redis.call('zrangebyscore', executing_key, '-inf', '('..now)\n\n if #expired == 0 then\n return redis.call('hget', settings_key, 'running')\n else\n redis.call('zremrangebyscore', executing_key, '-inf', '('..now)\n\n local args = {'hmget', running_key}\n for i = 1, #expired do\n table.insert(args, expired[i])\n end\n\n local weights = redis.call(unpack(args))\n\n args[1] = 'hdel'\n local deleted = redis.call(unpack(args))\n\n local total = 0\n for i = 1, #weights do\n total = total + (tonumber(weights[i]) or 0)\n end\n local incr = -total\n if total == 0 then\n incr = 0\n else\n local id = redis.call('hget', settings_key, 'id')\n redis.call('publish', 'bottleneck_'..id, 'freed:'..total)\n end\n\n return redis.call('hincrby', settings_key, 'running', incr)\n end\n\nend\n","register.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal index = ARGV[1]\nlocal weight = tonumber(ARGV[2])\nlocal expiration = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\nlocal minTime = tonumber(settings[4])\nlocal groupTimeout = tonumber(settings[5])\n\nif conditions_check(weight, maxConcurrent, running, reservoir) then\n\n if expiration ~= nil then\n redis.call('zadd', executing_key, now + expiration, index)\n end\n redis.call('hset', running_key, index, weight)\n redis.call('hincrby', settings_key, 'running', weight)\n\n local wait = math.max(nextRequest - now, 0)\n local newNextRequest = now + wait + minTime\n\n if reservoir == nil then\n redis.call('hset', settings_key,\n 'nextRequest', newNextRequest\n )\n else\n reservoir = reservoir - weight\n redis.call('hmset', settings_key,\n 'reservoir', reservoir,\n 'nextRequest', newNextRequest\n )\n end\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\n\n return {true, wait, reservoir}\n\nelse\n return {false}\nend\n","running.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\nlocal now = ARGV[1]\n\nreturn tonumber(refresh_running(executing_key, running_key, settings_key, now))\n","submit.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal queueLength = tonumber(ARGV[1])\nlocal weight = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'highWater',\n 'reservoir',\n 'nextRequest',\n 'strategy',\n 'unblockTime',\n 'penalty',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal highWater = tonumber(settings[2])\nlocal reservoir = tonumber(settings[3])\nlocal nextRequest = tonumber(settings[4])\nlocal strategy = tonumber(settings[5])\nlocal unblockTime = tonumber(settings[6])\nlocal penalty = tonumber(settings[7])\nlocal minTime = tonumber(settings[8])\nlocal groupTimeout = tonumber(settings[9])\n\nif maxConcurrent ~= nil and weight > maxConcurrent then\n return redis.error_reply('OVERWEIGHT:'..weight..':'..maxConcurrent)\nend\n\nlocal reachedHWM = (highWater ~= nil and queueLength == highWater\n and not (\n conditions_check(weight, maxConcurrent, running, reservoir)\n and nextRequest - now <= 0\n )\n)\n\nlocal blocked = strategy == 3 and (reachedHWM or unblockTime >= now)\n\nif blocked then\n local computedPenalty = penalty\n if computedPenalty == nil then\n if minTime == 0 then\n computedPenalty = 5000\n else\n computedPenalty = 15 * minTime\n end\n end\n\n local newNextRequest = now + computedPenalty + minTime\n\n redis.call('hmset', settings_key,\n 'unblockTime', now + computedPenalty,\n 'nextRequest', newNextRequest\n )\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\nend\n\nreturn {reachedHWM, blocked, strategy}\n","update_settings.lua":"local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal args = {'hmset', settings_key}\n\nfor i = 1, #ARGV do\n table.insert(args, ARGV[i])\nend\n\nredis.call(unpack(args))\n\nlocal groupTimeout = tonumber(redis.call('hget', settings_key, 'groupTimeout'))\nrefresh_expiration(executing_key, running_key, settings_key, 0, 0, groupTimeout)\n\nreturn {}\n","validate_keys.lua":"local settings_key = KEYS[1]\n\nif not (redis.call('exists', settings_key) == 1) then\n return redis.error_reply('SETTINGS_KEY_NOT_FOUND')\nend\n"}},{}],14:[function(require,module,exports){"use strict";(function(){exports.load=function(received,defaults,onto={}){var k,ref,v;for(k in defaults){v=defaults[k];onto[k]=(ref=received[k])!=null?ref:v}return onto};exports.overwrite=function(received,defaults,onto={}){var k,v;for(k in received){v=received[k];if(defaults[k]!==void 0){onto[k]=v}}return onto}}).call(undefined)},{}],15:[function(require,module,exports){module.exports={name:"bottleneck",version:"2.7.0",description:"Distributed task scheduler and rate limiter",main:"lib/index.js",typings:"bottleneck.d.ts",scripts:{test:"./node_modules/mocha/bin/mocha test",build:"./scripts/build.sh",compile:"./scripts/build.sh compile"},repository:{type:"git",url:"https://github.com/SGrondin/bottleneck"},keywords:["async rate limiter","rate limiter","rate limiting","async","rate","limiting","limiter","throttle","throttling","load","ddos"],author:{name:"Simon Grondin"},license:"MIT",bugs:{url:"https://github.com/SGrondin/bottleneck/issues"},devDependencies:{"@types/es6-promise":"0.0.33",assert:"1.4.x","babel-core":"^6.26.0","babel-preset-env":"^1.6.1",browserify:"*",coffeescript:"2.2.x","ejs-cli":"2.0.1",mocha:"4.x",redis:"^2.8.0",typescript:"^2.6.2","uglify-es":"3.x"}}},{}]},{},[12]); |
{ | ||
"name": "bottleneck", | ||
"main": "bottleneck.js", | ||
"version": "2.6.0", | ||
"version": "2.7.0", | ||
"homepage": "https://github.com/SGrondin/bottleneck", | ||
@@ -6,0 +6,0 @@ "authors": [ |
@@ -584,3 +584,4 @@ "use strict"; | ||
Promise: Promise, | ||
_groupTimeout: null | ||
timeout: null, | ||
_groupConnection: null | ||
}; | ||
@@ -587,0 +588,0 @@ |
@@ -7,3 +7,3 @@ "use strict"; | ||
(function () { | ||
var Events, Group, parser; | ||
var Events, Group, RedisConnection, parser; | ||
@@ -14,5 +14,8 @@ parser = require("./parser"); | ||
RedisConnection = require("./RedisConnection"); | ||
Group = function () { | ||
class Group { | ||
constructor(limiterOptions = {}, groupOptions = {}) { | ||
constructor(limiterOptions = {}) { | ||
var ref, ref1; | ||
this.key = this.key.bind(this); | ||
@@ -25,3 +28,3 @@ this.deleteKey = this.deleteKey.bind(this); | ||
this.limiterOptions = limiterOptions; | ||
parser.load(groupOptions, this.defaults, this); | ||
parser.load(this.limiterOptions, this.defaults, this); | ||
this.Events = new Events(this); | ||
@@ -31,2 +34,5 @@ this.instances = {}; | ||
this._startAutoCleanup(); | ||
if (this.limiterOptions.datastore === "redis") { | ||
this._connection = new RedisConnection((ref = this.limiterOptions.clientOptions) != null ? ref : {}, (ref1 = this.limiterOptions.Promise) != null ? ref1 : Promise, this.Events); | ||
} | ||
} | ||
@@ -40,3 +46,4 @@ | ||
id: `group-key-${key}`, | ||
_groupTimeout: this.timeout | ||
timeout: this.timeout, | ||
_groupConnection: this._connection | ||
})); | ||
@@ -103,2 +110,3 @@ this.Events.trigger("created", [limiter, key]); | ||
parser.overwrite(options, this.defaults, this); | ||
parser.overwrite(options, options, this.limiterOptions); | ||
if (options.timeout != null) { | ||
@@ -109,2 +117,7 @@ return this._startAutoCleanup(); | ||
disconnect(flush) { | ||
var ref; | ||
return (ref = this._connection) != null ? ref.disconnect(flush) : void 0; | ||
} | ||
}; | ||
@@ -111,0 +124,0 @@ |
@@ -65,3 +65,3 @@ "use strict"; | ||
yield _this3.yieldLoop(); | ||
return _this3._nextRequest + _this3._groupTimeout < time; | ||
return _this3._nextRequest + _this3.timeout < time; | ||
})(); | ||
@@ -68,0 +68,0 @@ } |
@@ -11,8 +11,8 @@ { | ||
"refresh_expiration.lua": "local refresh_expiration = function (executing_key, running_key, settings_key, now, nextRequest, groupTimeout)\n\n if groupTimeout ~= nil then\n local ttl = (nextRequest + groupTimeout) - now\n\n redis.call('pexpire', executing_key, ttl)\n redis.call('pexpire', running_key, ttl)\n redis.call('pexpire', settings_key, ttl)\n end\n\nend\n", | ||
"refresh_running.lua": "local refresh_running = function (executing_key, running_key, settings_key, now)\n\n local expired = redis.call('zrangebyscore', executing_key, '-inf', '('..now)\n\n if #expired == 0 then\n return redis.call('hget', settings_key, 'running')\n else\n redis.call('zremrangebyscore', executing_key, '-inf', '('..now)\n\n local args = {'hmget', running_key}\n for i = 1, #expired do\n table.insert(args, expired[i])\n end\n\n local weights = redis.call(unpack(args))\n\n args[1] = 'hdel'\n local deleted = redis.call(unpack(args))\n\n local total = 0\n for i = 1, #weights do\n total = total + (tonumber(weights[i]) or 0)\n end\n local incr = -total\n if total == 0 then\n incr = 0\n else\n local id = redis.call('hget', settings_key, 'id')\n redis.call('publish', 'b_'..id, 'freed:'..total)\n end\n\n return redis.call('hincrby', settings_key, 'running', incr)\n end\n\nend\n", | ||
"refresh_running.lua": "local refresh_running = function (executing_key, running_key, settings_key, now)\n\n local expired = redis.call('zrangebyscore', executing_key, '-inf', '('..now)\n\n if #expired == 0 then\n return redis.call('hget', settings_key, 'running')\n else\n redis.call('zremrangebyscore', executing_key, '-inf', '('..now)\n\n local args = {'hmget', running_key}\n for i = 1, #expired do\n table.insert(args, expired[i])\n end\n\n local weights = redis.call(unpack(args))\n\n args[1] = 'hdel'\n local deleted = redis.call(unpack(args))\n\n local total = 0\n for i = 1, #weights do\n total = total + (tonumber(weights[i]) or 0)\n end\n local incr = -total\n if total == 0 then\n incr = 0\n else\n local id = redis.call('hget', settings_key, 'id')\n redis.call('publish', 'bottleneck_'..id, 'freed:'..total)\n end\n\n return redis.call('hincrby', settings_key, 'running', incr)\n end\n\nend\n", | ||
"register.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal index = ARGV[1]\nlocal weight = tonumber(ARGV[2])\nlocal expiration = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'reservoir',\n 'nextRequest',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal reservoir = tonumber(settings[2])\nlocal nextRequest = tonumber(settings[3])\nlocal minTime = tonumber(settings[4])\nlocal groupTimeout = tonumber(settings[5])\n\nif conditions_check(weight, maxConcurrent, running, reservoir) then\n\n if expiration ~= nil then\n redis.call('zadd', executing_key, now + expiration, index)\n end\n redis.call('hset', running_key, index, weight)\n redis.call('hincrby', settings_key, 'running', weight)\n\n local wait = math.max(nextRequest - now, 0)\n local newNextRequest = now + wait + minTime\n\n if reservoir == nil then\n redis.call('hset', settings_key,\n 'nextRequest', newNextRequest\n )\n else\n reservoir = reservoir - weight\n redis.call('hmset', settings_key,\n 'reservoir', reservoir,\n 'nextRequest', newNextRequest\n )\n end\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\n\n return {true, wait, reservoir}\n\nelse\n return {false}\nend\n", | ||
"running.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\nlocal now = ARGV[1]\n\nreturn tonumber(refresh_running(executing_key, running_key, settings_key, now))\n", | ||
"submit.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal queueLength = tonumber(ARGV[1])\nlocal weight = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'highWater',\n 'reservoir',\n 'nextRequest',\n 'strategy',\n 'unblockTime',\n 'penalty',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal highWater = tonumber(settings[2])\nlocal reservoir = tonumber(settings[3])\nlocal nextRequest = tonumber(settings[4])\nlocal strategy = tonumber(settings[5])\nlocal unblockTime = tonumber(settings[6])\nlocal penalty = tonumber(settings[7])\nlocal minTime = tonumber(settings[8])\nlocal groupTimeout = tonumber(settings[9])\n\nif maxConcurrent ~= nil and weight > maxConcurrent then\n return redis.error_reply('OVERWEIGHT:'..weight..':'..maxConcurrent)\nend\n\nlocal reachedHWM = (highWater ~= nil and queueLength == highWater\n and not (\n conditions_check(weight, maxConcurrent, running, reservoir)\n and nextRequest - now <= 0\n )\n)\n\nlocal blocked = strategy == 3 and (reachedHWM or unblockTime >= now)\n\nif blocked then\n local computedPenalty = penalty\n if computedPenalty == nil then\n if minTime == 0 then\n computedPenalty = 5000\n else\n computedPenalty = 15 * minTime\n end\n end\n\n local newNextRequest = unblockTime + minTime\n\n redis.call('hmset', settings_key,\n 'unblockTime', now + computedPenalty,\n 'nextRequest', newNextRequest\n )\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\nend\n\nreturn {reachedHWM, blocked, strategy}\n", | ||
"submit.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal queueLength = tonumber(ARGV[1])\nlocal weight = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal running = tonumber(refresh_running(executing_key, running_key, settings_key, now))\nlocal settings = redis.call('hmget', settings_key,\n 'maxConcurrent',\n 'highWater',\n 'reservoir',\n 'nextRequest',\n 'strategy',\n 'unblockTime',\n 'penalty',\n 'minTime',\n 'groupTimeout'\n)\nlocal maxConcurrent = tonumber(settings[1])\nlocal highWater = tonumber(settings[2])\nlocal reservoir = tonumber(settings[3])\nlocal nextRequest = tonumber(settings[4])\nlocal strategy = tonumber(settings[5])\nlocal unblockTime = tonumber(settings[6])\nlocal penalty = tonumber(settings[7])\nlocal minTime = tonumber(settings[8])\nlocal groupTimeout = tonumber(settings[9])\n\nif maxConcurrent ~= nil and weight > maxConcurrent then\n return redis.error_reply('OVERWEIGHT:'..weight..':'..maxConcurrent)\nend\n\nlocal reachedHWM = (highWater ~= nil and queueLength == highWater\n and not (\n conditions_check(weight, maxConcurrent, running, reservoir)\n and nextRequest - now <= 0\n )\n)\n\nlocal blocked = strategy == 3 and (reachedHWM or unblockTime >= now)\n\nif blocked then\n local computedPenalty = penalty\n if computedPenalty == nil then\n if minTime == 0 then\n computedPenalty = 5000\n else\n computedPenalty = 15 * minTime\n end\n end\n\n local newNextRequest = now + computedPenalty + minTime\n\n redis.call('hmset', settings_key,\n 'unblockTime', now + computedPenalty,\n 'nextRequest', newNextRequest\n )\n\n refresh_expiration(executing_key, running_key, settings_key, now, newNextRequest, groupTimeout)\nend\n\nreturn {reachedHWM, blocked, strategy}\n", | ||
"update_settings.lua": "local settings_key = KEYS[1]\nlocal running_key = KEYS[2]\nlocal executing_key = KEYS[3]\n\nlocal args = {'hmset', settings_key}\n\nfor i = 1, #ARGV do\n table.insert(args, ARGV[i])\nend\n\nredis.call(unpack(args))\n\nlocal groupTimeout = tonumber(redis.call('hget', settings_key, 'groupTimeout'))\nrefresh_expiration(executing_key, running_key, settings_key, 0, 0, groupTimeout)\n\nreturn {}\n", | ||
"validate_keys.lua": "local settings_key = KEYS[1]\n\nif not (redis.call('exists', settings_key) == 1) then\n return redis.error_reply('SETTINGS_KEY_NOT_FOUND')\nend\n" | ||
} |
@@ -9,125 +9,39 @@ "use strict"; | ||
(function () { | ||
var BottleneckError, DLList, RedisStorage, libraries, lua, parser, scriptTemplates; | ||
var BottleneckError, RedisConnection, RedisStorage, Scripts, parser; | ||
parser = require("./parser"); | ||
DLList = require("./DLList"); | ||
BottleneckError = require("./BottleneckError"); | ||
lua = require("./lua.json"); | ||
RedisConnection = require("./RedisConnection"); | ||
libraries = { | ||
get_time: lua["get_time.lua"], | ||
refresh_running: lua["refresh_running.lua"], | ||
conditions_check: lua["conditions_check.lua"], | ||
refresh_expiration: lua["refresh_expiration.lua"], | ||
validate_keys: lua["validate_keys.lua"] | ||
}; | ||
Scripts = require("./Scripts"); | ||
scriptTemplates = function scriptTemplates(id) { | ||
return { | ||
init: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["refresh_expiration"], | ||
code: lua["init.lua"] | ||
}, | ||
update_settings: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_expiration"], | ||
code: lua["update_settings.lua"] | ||
}, | ||
running: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running"], | ||
code: lua["running.lua"] | ||
}, | ||
group_check: { | ||
keys: [`b_${id}_settings`], | ||
libs: [], | ||
code: lua["group_check.lua"] | ||
}, | ||
check: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running", "conditions_check"], | ||
code: lua["check.lua"] | ||
}, | ||
submit: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running", "conditions_check", "refresh_expiration"], | ||
code: lua["submit.lua"] | ||
}, | ||
register: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running", "conditions_check", "refresh_expiration"], | ||
code: lua["register.lua"] | ||
}, | ||
free: { | ||
keys: [`b_${id}_settings`, `b_${id}_running`, `b_${id}_executing`], | ||
libs: ["validate_keys", "refresh_running"], | ||
code: lua["free.lua"] | ||
}, | ||
current_reservoir: { | ||
keys: [`b_${id}_settings`], | ||
libs: ["validate_keys"], | ||
code: lua["current_reservoir.lua"] | ||
}, | ||
increment_reservoir: { | ||
keys: [`b_${id}_settings`], | ||
libs: ["validate_keys"], | ||
code: lua["increment_reservoir.lua"] | ||
} | ||
}; | ||
}; | ||
RedisStorage = class RedisStorage { | ||
constructor(instance, initSettings, options) { | ||
var redis; | ||
this.loadAll = this.loadAll.bind(this); | ||
var ref; | ||
this.instance = instance; | ||
this.initSettings = initSettings; | ||
redis = eval("require")("redis"); // Obfuscated or else Webpack/Angular will try to inline the optional redis module | ||
this.originalId = this.instance.id; | ||
this.scripts = scriptTemplates(this.originalId); | ||
parser.load(options, options, this); | ||
this.client = redis.createClient(this.clientOptions); | ||
this.subClient = redis.createClient(this.clientOptions); | ||
this.shas = {}; | ||
this.clients = { | ||
client: this.client, | ||
subscriber: this.subClient | ||
}; | ||
this.isReady = false; | ||
this.ready = new this.Promise((resolve, reject) => { | ||
var count, done, errorListener; | ||
errorListener = function errorListener(e) { | ||
return reject(e); | ||
}; | ||
count = 0; | ||
done = () => { | ||
count++; | ||
if (count === 2) { | ||
[this.client, this.subClient].forEach(client => { | ||
client.removeListener("error", errorListener); | ||
return client.on("error", e => { | ||
return this.instance.Events.trigger("error", [e]); | ||
}); | ||
}); | ||
return resolve(); | ||
} | ||
}; | ||
this.client.on("error", errorListener); | ||
this.client.on("ready", function () { | ||
return done(); | ||
}); | ||
this.subClient.on("error", errorListener); | ||
return this.subClient.on("ready", () => { | ||
this.subClient.on("subscribe", function () { | ||
return done(); | ||
}); | ||
return this.subClient.subscribe(`b_${this.originalId}`); | ||
}); | ||
}).then(this.loadAll).then(() => { | ||
this.connection = (ref = this._groupConnection) != null ? ref : new RedisConnection(this.clientOptions, this.Promise, this.instance.Events); | ||
this.ready = this.connection.ready.then(clients => { | ||
this.clients = clients; | ||
if (this.connection.loaded) { | ||
return this.Promise.resolve(); | ||
} else { | ||
return this.Promise.all(Scripts.names.map(k => { | ||
return this._loadScript(k); | ||
})); | ||
} | ||
}).then(() => { | ||
var args; | ||
this.subClient.on("message", (channel, message) => { | ||
this.connection.loaded = true; | ||
args = this.prepareInitSettings(options.clearDatastore); | ||
this.isReady = true; | ||
return this.runScript("init", args); | ||
}).then(() => { | ||
this.connection.addLimiter(this.instance, message => { | ||
var info, type; | ||
@@ -146,6 +60,2 @@ | ||
}); | ||
args = this.prepareInitSettings(options.clearDatastore); | ||
this.isReady = true; | ||
return this.runScript("init", args); | ||
}).then(results => { | ||
return this.clients; | ||
@@ -156,14 +66,13 @@ }); | ||
disconnect(flush) { | ||
this.client.end(flush); | ||
this.subClient.end(flush); | ||
return this; | ||
this.connection.removeLimiter(this.instance); | ||
if (this._groupConnection == null) { | ||
return this.connection.disconnect(flush); | ||
} | ||
} | ||
loadScript(name) { | ||
_loadScript(name) { | ||
return new this.Promise((resolve, reject) => { | ||
var payload; | ||
payload = this.scripts[name].libs.map(function (lib) { | ||
return libraries[lib]; | ||
}).join("\n") + this.scripts[name].code; | ||
return this.client.multi([["script", "load", payload]]).exec((err, replies) => { | ||
payload = Scripts.payload(name); | ||
return this.clients.client.multi([["script", "load", payload]]).exec((err, replies) => { | ||
if (err != null) { | ||
@@ -178,14 +87,28 @@ return reject(err); | ||
loadAll() { | ||
var k, v; | ||
return this.Promise.all(function () { | ||
var ref, results1; | ||
ref = this.scripts; | ||
results1 = []; | ||
for (k in ref) { | ||
v = ref[k]; | ||
results1.push(this.loadScript(k)); | ||
} | ||
return results1; | ||
}.call(this)); | ||
runScript(name, args) { | ||
var keys; | ||
if (!this.isReady) { | ||
return this.Promise.reject(new BottleneckError("This limiter is not done connecting to Redis yet. Wait for the '.ready()' promise to resolve before submitting requests.")); | ||
} else { | ||
keys = Scripts.keys(name, this.originalId); | ||
return new this.Promise((resolve, reject) => { | ||
var arr; | ||
arr = [this.shas[name], keys.length].concat(keys, args, function (err, replies) { | ||
if (err != null) { | ||
return reject(err); | ||
} | ||
return resolve(replies); | ||
}); | ||
this.instance.Events.trigger("debug", [`Calling Redis script: ${name}.lua`, args]); | ||
return this.clients.client.evalsha.bind(this.clients.client).apply({}, arr); | ||
}).catch(e => { | ||
if (e.message === "SETTINGS_KEY_NOT_FOUND") { | ||
return this.runScript("init", this.prepareInitSettings(false)).then(() => { | ||
return this.runScript(name, args); | ||
}); | ||
} else { | ||
return this.Promise.reject(e); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -221,3 +144,3 @@ | ||
version: this.instance.version, | ||
groupTimeout: this._groupTimeout | ||
groupTimeout: this.timeout | ||
})); | ||
@@ -228,30 +151,2 @@ args.unshift(clear ? 1 : 0); | ||
runScript(name, args) { | ||
var script; | ||
if (!this.isReady) { | ||
return this.Promise.reject(new BottleneckError("This limiter is not done connecting to Redis yet. Wait for the '.ready()' promise to resolve before submitting requests.")); | ||
} else { | ||
script = this.scripts[name]; | ||
return new this.Promise((resolve, reject) => { | ||
var arr; | ||
arr = [this.shas[name], script.keys.length].concat(script.keys, args, function (err, replies) { | ||
if (err != null) { | ||
return reject(err); | ||
} | ||
return resolve(replies); | ||
}); | ||
this.instance.Events.trigger("debug", [`Calling Redis script: ${name}.lua`, args]); | ||
return this.client.evalsha.bind(this.client).apply({}, arr); | ||
}).catch(e => { | ||
if (e.message === "SETTINGS_KEY_NOT_FOUND") { | ||
return this.runScript("init", this.prepareInitSettings(false)).then(() => { | ||
return this.runScript(name, args); | ||
}); | ||
} else { | ||
return this.Promise.reject(e); | ||
} | ||
}); | ||
} | ||
} | ||
convertBool(b) { | ||
@@ -258,0 +153,0 @@ return !!b; |
{ | ||
"name": "bottleneck", | ||
"version": "2.6.0", | ||
"version": "2.7.0", | ||
"description": "Distributed task scheduler and rate limiter", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -346,3 +346,3 @@ # bottleneck | ||
```js | ||
limiter.on('error', function (error) { | ||
limiter.on("error", function (error) { | ||
// This is where Bottleneck's errors end up. | ||
@@ -358,3 +358,3 @@ }) | ||
```js | ||
limiter.on('empty', function () { | ||
limiter.on("empty", function () { | ||
// This will be called when `limiter.empty()` becomes true. | ||
@@ -366,3 +366,3 @@ }) | ||
```js | ||
limiter.on('idle', function () { | ||
limiter.on("idle", function () { | ||
// This will be called when `limiter.empty()` is `true` and `limiter.running()` is `0`. | ||
@@ -374,3 +374,3 @@ }) | ||
```js | ||
limiter.on('dropped', function (dropped) { | ||
limiter.on("dropped", function (dropped) { | ||
// This will be called when a strategy was triggered. | ||
@@ -383,3 +383,3 @@ // The dropped request is passed to this event listener. | ||
```js | ||
limiter.on('depleted', function (empty) { | ||
limiter.on("depleted", function (empty) { | ||
// This will be called every time the reservoir drops to 0. | ||
@@ -392,3 +392,3 @@ // The `empty` (boolean) argument indicates whether `limiter.empty()` is currently true. | ||
```js | ||
limiter.on('debug', function (message, data) { | ||
limiter.on("debug", function (message, data) { | ||
// Useful to figure out what the limiter is doing in real time | ||
@@ -443,3 +443,3 @@ // and to help debug your application | ||
.then(() => { | ||
console.log('Shutdown completed!') | ||
console.log("Shutdown completed!") | ||
}) | ||
@@ -512,2 +512,3 @@ ``` | ||
| `clientOptions` | `{}` | This object is passed directly to NodeRedis's `redis.createClient()` method. [See all the valid client options.](https://github.com/NodeRedis/node_redis#options-object-properties) | | ||
| `timeout` | `null` | The Redis TTL in milliseconds ([TTL](https://redis.io/commands/ttl)) for the keys created by the limiter. When `timeout` is set, the limiter's state will be automatically removed from Redis after `timeout` milliseconds of inactivity. **Note:** `timeout` is `300000` (5 minutes) by default when using a Group. | | ||
@@ -576,3 +577,3 @@ ###### `.ready()` | ||
It is **strongly recommended** to [set up an `error` listener](#events). | ||
It is **strongly recommended** to [set up an `error` listener](#events) on all your limiters and on your Groups. | ||
@@ -599,4 +600,6 @@ Bottleneck does not guarantee that the concurrency will be spread evenly across limiters. With `{ maxConcurrent: 5 }`, it's absolutely possible for a single limiter to end up running 5 jobs simultaneously while the other limiters in the cluster sit idle. To spread the load, use the `.chain()` method: | ||
- At the moment, each limiter opens 2 connections to Redis. This can lead to a high number of connections, especially when Groups are used. This might change in a future release. | ||
- Bottleneck is compatible with [Redis Clusters](https://redis.io/topics/cluster-tutorial). | ||
- As of v2.7.0, each Group will create 2 connections to Redis, one for commands and one for pub/sub. All limiters within the Group will share those connections. | ||
- Each standalone limiter has its own 2 connections. | ||
- Redis connectivity errors trigger an `error` event on the owner of the connection (the Group or the limiter). | ||
- Bottleneck itself is compatible with [Redis Clusters](https://redis.io/topics/cluster-tutorial), but `NodeRedis` may not support it at the moment. Future versions of Bottleneck will support the `ioredis` driver. | ||
- Bottleneck's data is stored in Redis keys starting with `b_`. It also uses pub/sub channels starting with `b_` It will not interfere with any other data stored on the server. | ||
@@ -608,5 +611,5 @@ - Bottleneck loads a few Lua scripts on the Redis server using the `SCRIPT LOAD` command. These scripts only take up a few Kb of memory. Running the `SCRIPT FLUSH` command will cause any connected limiters to experience critical errors until a new limiter connects to Redis and loads the scripts again. | ||
- When using Groups, the `timeout` option is set to `300000` milliseconds by default. | ||
- Call `group.disconnect()` to permanently close a Group's Redis connections. It takes an optional boolean argument, pass `false` to forcefully close the connections without waiting. | ||
- If you are using a Group, the generated limiters automatically receive an `id` with the pattern `group-key-${KEY}`. | ||
- A Group collects its own garbage, and so when using Clustering, it manages the Redis TTL ([TTL](https://redis.io/commands/ttl)) on the keys it uses to ensure they get cleaned up by Redis when unused for longer than the Group's `timeout` setting. | ||
- Each limiter opens 2 connections to Redis. Be careful not to go over [Redis' `maxclients` value](https://redis.io/topics/clients). | ||
@@ -618,3 +621,3 @@ | ||
If your application is not behaving as expected, start by making sure you're catching `error` [events emitted](#events) by your limiters. Those errors are most likely uncaught exceptions from your application code. | ||
If your application is not behaving as expected, start by making sure you're catching `error` [events emitted](#events) by your limiters and your Groups. Those errors are most likely uncaught exceptions from your application code. | ||
@@ -654,3 +657,3 @@ To see exactly what a limiter is doing in real time, listen to the `debug` event. It contains detailed information about how the limiter is executing your code. Adding [job IDs](#job-options) to all your jobs makes the debug output more readable. | ||
Let's take a DNS server as an example of how Bottleneck can be used. It's a service that sees a lot of abuse and where incoming DNS requests need to be rate limited. Bottleneck is so tiny, it's acceptable to create one limiter for each origin IP, even if it means creating thousands of limiters. The `Group` feature is perfect for this use case. Create one group and use the origin IP to rate limit each IP independently. Each call with the same key (IP) will be routed to the same underlying limiter. A group is created like a limiter: | ||
Let's take a DNS server as an example of how Bottleneck can be used. It's a service that sees a lot of abuse and where incoming DNS requests need to be rate limited. Bottleneck is so tiny, it's acceptable to create one limiter for each origin IP, even if it means creating thousands of limiters. The `Group` feature is perfect for this use case. Create one Group and use the origin IP to rate limit each IP independently. Each call with the same key (IP) will be routed to the same underlying limiter. A Group is created like a limiter: | ||
@@ -662,5 +665,5 @@ | ||
The `options` object will be used for every limiter created by the group. | ||
The `options` object will be used for every limiter created by the Group. | ||
The group is then used with the `.key(str)` method: | ||
The Group is then used with the `.key(str)` method: | ||
@@ -676,12 +679,14 @@ ```js | ||
The return value of `.key(str)` is a limiter. If it doesn't already exist, it is generated for you. Calling `key()` is how limiters are created inside a Group. Limiters that have been idle for longer than 5 minutes are deleted to avoid memory leaks. | ||
The return value of `.key(str)` is a limiter. If it doesn't already exist, it is generated for you. Calling `key()` is how limiters are created inside a Group. | ||
__on('created')__ | ||
Limiters that have been idle for longer than 5 minutes are deleted to avoid memory leaks, this value can be changed by passing a different `timeout` option, in milliseconds. | ||
__on("created")__ | ||
```js | ||
group.on('created', (limiter, key) => { | ||
console.log('A new limiter was created for key: ' + key) | ||
group.on("created", (limiter, key) => { | ||
console.log("A new limiter was created for key: " + key) | ||
// Prepare the limiter, for example we'll want to listen to its 'error' events! | ||
limiter.on('error', (err) => { | ||
limiter.on("error", (err) => { | ||
// Handle errors here | ||
@@ -701,10 +706,9 @@ }) | ||
```js | ||
group.updateSettings({ timeout: 60000 }) | ||
const group = new Bottleneck.Group({ maxConcurrent: 2, minTime: 250 }) | ||
group.updateSettings({ minTime: 500 }) | ||
``` | ||
* `timeout`: The expiration time for unused limiters, in milliseconds. By default, it is `300000` (5 minutes). | ||
After executing the above commands, new limiters will be created with `{ maxConcurrent: 2, minTime: 500 }`. | ||
When autocleanup is enabled, limiters not used in the last `timeout` milliseconds will be deleted to avoid memory leaks. | ||
__deleteKey()__ | ||
@@ -719,3 +723,3 @@ | ||
Returns an array containing all the keys in the group. | ||
Returns an array containing all the keys in the Group. | ||
@@ -756,3 +760,3 @@ | ||
- The `Group` constructor takes an options object to match the limiter constructor. | ||
- Renamed the `Group` `changeTimeout()` method to `updateSettings()`, it now takes an options object. See [Group](#group). | ||
- Removed the `Group` `changeTimeout()` method. Use `updateSettings()` instead, it now takes an options object. See [Group](#group). | ||
@@ -759,0 +763,0 @@ Version 2 is more user-friendly, powerful and reliable. |
@@ -61,3 +61,3 @@ var makeTest = require('./context') | ||
var limiter = group.key(key) | ||
c.mustEqual(limiter._store._groupTimeout, group.timeout) | ||
c.mustEqual(limiter._store.timeout, group.timeout) | ||
return limiter.id | ||
@@ -142,3 +142,3 @@ }) | ||
it('Should update its settings', function () { | ||
it('Should update its timeout', function () { | ||
c = makeTest() | ||
@@ -149,4 +149,4 @@ var group1 = new Bottleneck.Group({ | ||
var group2 = new Bottleneck.Group({ | ||
maxConcurrent: 1, minTime: 100 | ||
}, { timeout: 5000}) | ||
maxConcurrent: 1, minTime: 100, timeout: 5000 | ||
}) | ||
@@ -165,2 +165,18 @@ c.mustEqual(group1.timeout, 300000) | ||
it('Should update its limiter options', function () { | ||
c = makeTest() | ||
var group = new Bottleneck.Group({ | ||
maxConcurrent: 1, minTime: 100 | ||
}) | ||
var limiter1 = group.key('AAA') | ||
c.mustEqual(limiter1._store.minTime, 100) | ||
group.updateSettings({ minTime: 200 }) | ||
c.mustEqual(limiter1._store.minTime, 100) | ||
var limiter2 = group.key('BBB') | ||
c.mustEqual(limiter2._store.minTime, 200) | ||
}) | ||
it('Should support keys() and limiters()', function () { | ||
@@ -194,3 +210,3 @@ c = makeTest() | ||
group.updateSettings({ timeout: 50 }) | ||
c = makeTest({ id: 'something', _groupTimeout: group.timeout }) | ||
c = makeTest({ id: 'something', timeout: group.timeout }) | ||
@@ -197,0 +213,0 @@ return c.limiter.ready() |
@@ -153,2 +153,3 @@ var makeTest = require('./context') | ||
var called = 0 | ||
c.limiter.on('dropped', function (dropped) { | ||
@@ -155,0 +156,0 @@ c.mustExist(dropped.task) |
var makeTest = require('./context') | ||
var Bottleneck = require('../lib/index.js') | ||
var Scripts = require('../lib/Scripts.js') | ||
var assert = require('assert') | ||
@@ -27,2 +28,34 @@ var packagejson = require('../package.json') | ||
it('Should not have a key TTL by default for standalone limiters', function () { | ||
c = makeTest() | ||
return c.limiter.ready() | ||
.then(function () { | ||
return new Promise(function (resolve, reject) { | ||
var settings_key = Scripts.keys("update_settings", c.limiter._store.originalId)[0] | ||
c.limiter._store.clients.client.ttl(settings_key, function (err, ttl) { | ||
if (err != null) return reject(err) | ||
assert(ttl < 0) | ||
return resolve() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('Should allow timeout setting for standalone limiters', function () { | ||
c = makeTest({ timeout: 5 * 60 * 1000 }) | ||
return c.limiter.ready() | ||
.then(function () { | ||
return new Promise(function (resolve, reject) { | ||
var settings_key = Scripts.keys("update_settings", c.limiter._store.originalId)[0] | ||
c.limiter._store.clients.client.ttl(settings_key, function (err, ttl) { | ||
if (err != null) return reject(err) | ||
assert(ttl >= 290 && ttl <= 305) | ||
return resolve() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('Should publish running decreases', function () { | ||
@@ -57,4 +90,4 @@ c = makeTest({ maxConcurrent: 2 }) | ||
return new Promise(function (resolve, reject) { | ||
var settings_key = limiter2._store.scripts.update_settings.keys[0] | ||
limiter2._store.client.hget(settings_key, 'version', function (err, data) { | ||
var settings_key = Scripts.keys("update_settings", limiter2._store.originalId)[0] | ||
limiter2._store.clients.client.hget(settings_key, 'version', function (err, data) { | ||
if (err != null) return reject(err) | ||
@@ -210,7 +243,7 @@ c.mustEqual(data, packagejson.version) | ||
.then(function () { | ||
var settings_key = limiter._store.scripts.update_settings.keys[0] | ||
var settings_key = Scripts.keys("update_settings", limiter._store.originalId)[0] | ||
assert(settings_key.indexOf(randomId) > 0) | ||
return new Promise(function (resolve, reject) { | ||
limiter._store.client.del(settings_key, function (err, data) { | ||
limiter._store.clients.client.del(settings_key, function (err, data) { | ||
if (err != null) return reject(err) | ||
@@ -238,6 +271,6 @@ return resolve(data) | ||
c.mustEqual(running, 0) | ||
var settings_key = limiter._store.scripts.update_settings.keys[0] | ||
var settings_key = Scripts.keys("update_settings", limiter._store.originalId)[0] | ||
return new Promise(function (resolve, reject) { | ||
limiter._store.client.del(settings_key, function (err, data) { | ||
limiter._store.clients.client.del(settings_key, function (err, data) { | ||
if (err != null) return reject(err) | ||
@@ -259,2 +292,28 @@ return resolve(data) | ||
it('Should have a default key TTL when using Groups', function () { | ||
c = makeTest() | ||
var group = new Bottleneck.Group({ | ||
datastore: 'redis' | ||
}) | ||
return c.limiter.ready() | ||
.then(function () { | ||
return group.key('one').ready() | ||
}) | ||
.then(function () { | ||
return new Promise(function (resolve, reject) { | ||
var limiter = group.key('one') | ||
var settings_key = Scripts.keys("update_settings", limiter._store.originalId)[0] | ||
limiter._store.clients.client.ttl(settings_key, function (err, ttl) { | ||
if (err != null) return reject(err) | ||
assert(ttl >= 290 && ttl <= 305) | ||
return resolve() | ||
}) | ||
}) | ||
}) | ||
.then(function () { | ||
group.disconnect(false) | ||
}) | ||
}) | ||
it('Should support Groups and expire Redis keys', function () { | ||
@@ -265,4 +324,5 @@ c = makeTest() | ||
clearDatastore: true, | ||
minTime: 50 | ||
}, { timeout: 200 }) | ||
minTime: 50, | ||
timeout: 200 | ||
}) | ||
var limiter1 | ||
@@ -273,7 +333,7 @@ var limiter2 | ||
var limiterKeys = function (limiter) { | ||
return limiter._store.scripts.init.keys | ||
return Scripts.keys("init", limiter._store.originalId)[0] | ||
} | ||
var keysExist = function (keys) { | ||
return new Promise(function (resolve, reject) { | ||
return c.limiter._store.client.exists(...keys, function (err, data) { | ||
return c.limiter._store.clients.client.exists(...keys, function (err, data) { | ||
if (err != null) { | ||
@@ -331,3 +391,3 @@ return reject(err) | ||
return c.wait(300) | ||
return c.wait(400) | ||
}) | ||
@@ -341,8 +401,6 @@ .then(function () { | ||
c.mustEqual(exist, 0) | ||
c.mustEqual(group.keys().length, 0) | ||
c.mustEqual(Object.keys(group._connection.pubsubs).length, 0) | ||
group.disconnect(false) | ||
}) | ||
.then(function () { | ||
group.keys().forEach(function (key) { | ||
group.key(key).disconnect(false) | ||
}) | ||
}) | ||
@@ -349,0 +407,0 @@ }) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
345646
63
5266
775
2