{"_id":"@cjpais/redis-gcra","_rev":"2-469d3e3ee980c76f78f61538736375ed","name":"@cjpais/redis-gcra","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@cjpais/redis-gcra","version":"0.1.0","description":"Rate limiting based on Generic Cell Rate Algorithm. Modified to include monthly limit support","main":"lib/index.js","types":"index.d.ts","homepage":"https://github.com/cjpais/redis-gcra#readme","author":{"name":"CJ Pais","email":"cj@cjpais.com"},"license":"MIT","keywords":["GCRA","redis","limit","throttle"],"bugs":{"url":"https://github.com/cjpais/redis-gcra/issues"},"directories":{"test":"./test","example":"./examples","lib":"./lib"},"engines":{"node":">=14"},"scripts":{"lint":"esw . --ext .js","lint:fix":"yarn lint --fix","lint:watch":"yarn lint --watch","lint:changed":"lint-staged","test":"mocha test 2>&1","test:watch":"mocha --watch test 2>&1","reinstall":"rm -rf node_modules && yarn install","prepare":"husky install"},"repository":{"type":"git","url":"git+https://github.com/cjpais/redis-gcra.git"},"lint-staged":{"*.js":"esw"},"devDependencies":{"@losant/eslint-config-losant":"^1.6.1","husky":"^8.0.3","ioredis":">=4.0.0 <6.0.0","lint-staged":"^15.0.2","mocha":"^10.2.0","redis":">=4.1.0 <5.0.0","rewire":"^7.0.0","should":"^13.2.3","sinon":"^17.0.1"},"eslintConfig":{"extends":"@losant/eslint-config-losant/env/node"},"mocha":{"reporter":"spec","recursive":true,"require":"should","check-leaks":true},"_id":"@cjpais/redis-gcra@0.1.0","gitHead":"e604bb9e410f47ac68744f0c710d90d8c02b4ed1","_nodeVersion":"20.12.2","_npmVersion":"10.5.0","dist":{"integrity":"sha512-IBfE/f9WuGMjwvZPCPcFm1wkNl9+vPIGKQhR4iMHYhVj+wMjtVNuIFIRUvYiC/3YWLue8On4XkLakVzvcRhvCA==","shasum":"25eaad954be14269c2973e134363ab7e943077ec","tarball":"https://registry.npmjs.org/@cjpais/redis-gcra/-/redis-gcra-0.1.0.tgz","fileCount":15,"unpackedSize":281023,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBYBWJXCcBAbnshRc6rszC8efuY/7bmiaAArFMxk+pOQIhANXOlq5VgD8LxKv5F6uxof6KtPQLW8b3Yozh8VWiOL5d"}]},"_npmUser":{"name":"cjpais","email":"cjpaisdev@gmail.com"},"maintainers":[{"name":"cjpais","email":"cjpaisdev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/redis-gcra_0.1.0_1714580437229_0.770892678948438"},"_hasShrinkwrap":false},"0.1.1":{"name":"@cjpais/redis-gcra","version":"0.1.1","description":"Rate limiting based on Generic Cell Rate Algorithm. Modified to include monthly limit support","main":"lib/index.js","types":"index.d.ts","homepage":"https://github.com/cjpais/redis-gcra#readme","author":{"name":"CJ Pais","email":"cj@cjpais.com"},"license":"MIT","keywords":["GCRA","redis","limit","throttle"],"bugs":{"url":"https://github.com/cjpais/redis-gcra/issues"},"directories":{"test":"./test","example":"./examples","lib":"./lib"},"engines":{"node":">=14"},"scripts":{"lint":"esw . --ext .js","lint:fix":"yarn lint --fix","lint:watch":"yarn lint --watch","lint:changed":"lint-staged","test":"mocha test 2>&1","test:watch":"mocha --watch test 2>&1","reinstall":"rm -rf node_modules && yarn install","prepare":"husky install"},"repository":{"type":"git","url":"git+https://github.com/cjpais/redis-gcra.git"},"lint-staged":{"*.js":"esw"},"devDependencies":{"@losant/eslint-config-losant":"^1.6.1","husky":"^8.0.3","ioredis":">=4.0.0 <6.0.0","lint-staged":"^15.0.2","mocha":"^10.2.0","redis":">=4.1.0 <5.0.0","rewire":"^7.0.0","should":"^13.2.3","sinon":"^17.0.1"},"eslintConfig":{"extends":"@losant/eslint-config-losant/env/node"},"mocha":{"reporter":"spec","recursive":true,"require":"should","check-leaks":true},"_id":"@cjpais/redis-gcra@0.1.1","gitHead":"fb39ef7092404e4b382a4bd879343f8b63f03d40","_nodeVersion":"20.12.2","_npmVersion":"10.5.0","dist":{"integrity":"sha512-8BT3/KdPeDouF80iX35w9AcDYZ1aXiHeVjV+QRcpUjcvBFyKjA5ErhXWyACSPqKN3hRXOb5s17iaqXw7vYtyCw==","shasum":"c5983691b4bbf7bc02a12d6f65dc7659dcc125e1","tarball":"https://registry.npmjs.org/@cjpais/redis-gcra/-/redis-gcra-0.1.1.tgz","fileCount":15,"unpackedSize":280910,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCDnFX/X/Y7xuqHdT6eSPdvmwxJg9bUWx1saEbgYGlEhQIgOeH3NiK91Shw3k14rVYH0puoPzGeL9M7FvYjC8MjFto="}]},"_npmUser":{"name":"cjpais","email":"cjpaisdev@gmail.com"},"maintainers":[{"name":"cjpais","email":"cjpaisdev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/redis-gcra_0.1.1_1714580534149_0.04644283332300825"},"_hasShrinkwrap":false},"0.1.2":{"name":"@cjpais/redis-gcra","version":"0.1.2","description":"Rate limiting based on Generic Cell Rate Algorithm. Modified to include monthly limit support","main":"lib/index.js","types":"index.d.ts","homepage":"https://github.com/cjpais/redis-gcra#readme","author":{"name":"CJ Pais","email":"cj@cjpais.com"},"license":"MIT","keywords":["GCRA","redis","limit","throttle"],"bugs":{"url":"https://github.com/cjpais/redis-gcra/issues"},"directories":{"test":"./test","example":"./examples","lib":"./lib"},"engines":{"node":">=14"},"scripts":{"lint":"esw . --ext .js","lint:fix":"yarn lint --fix","lint:watch":"yarn lint --watch","lint:changed":"lint-staged","test":"mocha test 2>&1","test:watch":"mocha --watch test 2>&1","reinstall":"rm -rf node_modules && yarn install","prepare":"husky install"},"repository":{"type":"git","url":"git+https://github.com/cjpais/redis-gcra.git"},"lint-staged":{"*.js":"esw"},"devDependencies":{"@losant/eslint-config-losant":"^1.6.1","husky":"^8.0.3","ioredis":">=4.0.0 <6.0.0","lint-staged":"^15.0.2","mocha":"^10.2.0","redis":">=4.1.0 <5.0.0","rewire":"^7.0.0","should":"^13.2.3","sinon":"^17.0.1"},"eslintConfig":{"extends":"@losant/eslint-config-losant/env/node"},"mocha":{"reporter":"spec","recursive":true,"require":"should","check-leaks":true},"_id":"@cjpais/redis-gcra@0.1.2","gitHead":"fee7fcd4579a00deffe85ca7cd3a1a3097b42c9f","_nodeVersion":"20.12.2","_npmVersion":"10.5.0","dist":{"integrity":"sha512-XjFiP4NFJSRd6g3KBNSG6GAQrmFreIrS1Efxiz9CIONd/pxw73KiQh760JnfCqusML2Pq/GgGSEevGBg5xgYTA==","shasum":"1eac521acead0d191b68cae1583fb5478617cdac","tarball":"https://registry.npmjs.org/@cjpais/redis-gcra/-/redis-gcra-0.1.2.tgz","fileCount":15,"unpackedSize":280911,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDF56hw3M/dYWTzxOBwlWW/+XmoepOuTcTFvwh/UTp5lAiBjNfX6txADOTgeNYStnHTJzCyjXYDjEaNZxAD1idS1cA=="}]},"_npmUser":{"name":"cjpais","email":"cjpaisdev@gmail.com"},"maintainers":[{"name":"cjpais","email":"cjpaisdev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/redis-gcra_0.1.2_1714580741715_0.9917216926438461"},"_hasShrinkwrap":false}},"time":{"created":"2024-05-01T16:20:37.148Z","0.1.0":"2024-05-01T16:20:37.499Z","modified":"2024-05-01T16:25:42.035Z","0.1.1":"2024-05-01T16:22:14.345Z","0.1.2":"2024-05-01T16:25:41.850Z"},"maintainers":[{"name":"cjpais","email":"cjpaisdev@gmail.com"}],"description":"Rate limiting based on Generic Cell Rate Algorithm. Modified to include monthly limit support","homepage":"https://github.com/cjpais/redis-gcra#readme","keywords":["GCRA","redis","limit","throttle"],"repository":{"type":"git","url":"git+https://github.com/cjpais/redis-gcra.git"},"author":{"name":"CJ Pais","email":"cj@cjpais.com"},"bugs":{"url":"https://github.com/cjpais/redis-gcra/issues"},"license":"MIT","readme":"# Node Redis GCRA Library with Monthly Limit\n\n[![NPM version](https://img.shields.io/npm/v/@cjpais/redis-gcra.svg)](https://www.npmjs.com/package/@cjpais/redis-gcra)\n\nThis is a fork of [redis-gcra](https://www.npmjs.com/package/redis-gcra). This fork\nadds support for a Monthly limit in addition to the GCRA limit. It is built into\nthe same lua script for performance/latency reasons.\n\n# Original README below (with slight modifications)\n\nThis module is an implementation of [GCRA](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) for rate limiting based on [Redis](https://redis.io/).\n\n* [Installation](#installation)\n* [API Documentation](#api-documentation)\n  * [Initializer](#redisgcra-redis-keyprefix-burst-rate-period-cost-)\n  * [Instance Functions](#instance-functions)\n    * [limit](#limit-key-burst-rate-period-cost-)\n    * [peek](#peek-key-burst-rate-period-)\n    * [reset](#reset)\n* [Node-redis usage](#node-redis-usage)\n* [Example](#example)\n* [Inspiration](#inspiration)\n* [License](#license)\n\n\n## Installation\n\n```bash\nnpm install redis-gcra\n```\n\nor\n\n```bash\nyarn install redis-gcra\n```\n\n## API Documentation\n\n### RedisGCRA({ redis, keyPrefix, burst, rate, period, cost, monthlyLimit })\n\n```javascript\nconst RedisGCRA = require('redis-gcra');\n\nconst limiter = RedisGCRA({\n  redis: undefined,\n  keyPrefix: '',\n  burst: 60,\n  rate: 1,\n  period: 1000,\n  cost: 1,\n  monthlyLimit: 1000\n});\n```\n\nThe redis-gcra module exposes a single function, which returns a limiter instance when called.\nIt takes the following options:\n\n| Option | Type | Default | Description |\n| :----- | :--- | ------- | :---------- |\n| redis | [ioredis](https://www.npmjs.com/package/ioredis) or [node-redis](https://www.npmjs.com/package/redis) instance* | | **Required.** The Redis client to be used. _(* If you use node-redis, additional setup work is required; see below. You may also use a Redis-client-like wrapper that exposes the [defineCommand](https://www.npmjs.com/package/ioredis#lua-scripting) method and the Redis `del` method.)_ |\n| keyPrefix | String | | A prefix for any keys that this limiter instance tries to create/access. |\n| burst | Number | 60 | The default burst value for this limiter instance. If provided, must be a number greater than or equal to 1. |\n| rate | Number | 1 | The default rate value for this limiter instance. If provided, must be a number greater than or equal to 1. |\n| period | Number | 1000 | The default period value for this limiter instance (milliseconds). If provided, must be a number greater than or equal to 1. |\n| cost | Number | 1 | The default cost value for for this limiter instance. If provided, must be a number greater than or equal to 0. |\n| monthlyLimit | Number | -1 | The maximum number of requests in a month for this key. -1 means unlimited. |\n\n### Instance Functions\n\n#### limit({ key, burst, rate, period, cost })\n\n```javascript\nlimiter.limit({\n  key: 'user/myUser@example.com',\n  burst: 1000,\n  rate: 1,\n  period: 1000,\n  cost: 2,\n  monthlyLimit: 1000\n});\n```\n\nIn order to perform rate limiting, you need to call the `limit` method. This function will attempt to consume the given `cost` from the token pool for this key.\nIf there are not enough tokens available for the given cost, no tokens will be consumed.\nIt takes the following options:\n\n| Option | Type | Description |\n| :----- | :--- | :---------- |\n| key | String | **Required.** The key to limit/throttle on. The actual Redis key will be prefixed with any `keyPrefix` given when the limiter was created. |\n| burst | Number | The maximum number of tokens available (i.e., token regeneration stops when this number is reached). If not provided, defaults to the `burst` value provided when the limiter was created. If provided, must be a number greater than or equal to 1. |\n| rate | Number | The rate at which tokens regenerate over the given `period`. If not provided, defaults to the `rate` value provided when the limiter was created. If provided, must be a number greater than or equal to 1. |\n| period | Number | The period (in milliseconds) over which tokens are regenerated at `rate`. If not provided, defaults to the `period` value provided when the limiter was created. If provided, must be a number greater than or equal to 1. |\n| cost | Number | The cost in tokens of this limit call. If not provided, defaults to the `cost` value provided when the limiter was created. If provided, must be a number greater than or equal to 0. |\n\nThe `limit` call returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), which will resolve to an object with the following properties:\n\n| Property | Type | Description |\n| :------  | :--- | :---------- |\n| limited | Boolean | Represents whether the given limit request was fulfilled. |\n| remaining | Number | The number of tokens remaining after this limit request. A request may be limited even if there are tokens available, if the cost was higher than the tokens available. If `limited` is true, `remaining` will be the number of tokens currently available _without_ the requested cost being subtracted. |\n| retryIn | Number | The number of milliseconds to wait until the given request would be allowed. Will always be 0 if `limited` is false, and greater than 0 if `limited` is true. If the `cost` was higher than `burst`, it is never possible to fulfill the request, and so `retryIn` will be `Infinity`. |\n| resetIn | Number | The number of milliseconds until the token pool has regenerated to `burst`. Will be 0 if the token pool is already at the `burst` value. |\n\n#### peek({ key, burst, rate, period })\n\n```javascript\nlimiter.peek({\n  key: 'user/myUser@example.com',\n  burst: 1000,\n  rate: 1,\n  period: 1000\n});\n```\n\nThe peek function allows you to look at the given state of the token pool for the given key. It\nwill not consume any tokens. It takes the following options:\n\n| Option | Type | Description |\n| :----- | :--- | :---------- |\n| key | String | **Required.**  The limiter key to peek at. The actual Redis key will be prefixed with any `keyPrefix` given when the limiter was created. |\n| burst | Number | The maximum number of tokens available (i.e., token regeneration stops when this number is reached). If not provided, defaults to the `burst` value provided when the limiter was created. If provided, must be a number greater than or equal to 1. |\n| rate | Number | The rate at which tokens regenerate over the given `period`. If not provided, defaults to the `rate` value provided when the limiter was created. If provided, must be a number greater than or equal to 1. |\n| period | Number |  The period (in milliseconds) over which tokens are regenerated at `rate`. If not provided, defaults to the `period` value provided when the limiter was created. If provided, must be a number greater than or equal to 1. |\n\nThe `peek` call returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), which will resolve to an object with the following properties:\n\n| Property | Type | Description |\n| :------- | :--- | :---------- |\n| limited | Boolean | Represents if the given key is currently limited. This will be `true` only if there are no tokens in the pool. |\n| remaining | Number | The number of tokens remaining in the given key's token pool. |\n| resetIn | Number | The number of milliseconds until the token pool has regenerated to `burst`. Will be 0 if the token pool is already at the `burst` value. |\n\n#### reset\n\n```javascript\nlimiter.reset({\n  key: 'user/myUser@example.com'\n});\n```\n\nThe reset function allows you to reset the token pool for the given key. In essence, this just\ndeletes the relevant key in Redis, which means the key is immediately back to having `burst` tokens\navailable. It takes the following options:\n\n| Option | Type | Description |\n| :----- | :--- | :---------- |\n| key | String | **Required.** The limiter key to reset. The actual Redis key will be prefixed with any `keyPrefix` given when the limiter was created. |\n\nThe `reset` call returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), which will resolve to a Boolean. If the result is `true`, the key was reset. If the result is `false`, the key did not need to be reset; there were already `burst` tokens available.\n\n## Node-redis usage\n\nTo use this module with [node-redis](https://www.npmjs.com/package/redis), you must include the include the exported `RedisGCRA.defineNodeRedisScripts` script as part of the `scripts` definition in your `createClient` configuration:\n\n```js\nconst Redis = require('redis');\nconst RedisGCRA = require('redis-gcra');\n\nconst redis = Redis.createClient({\n      scripts: {\n        ...RedisGCRA.defineNodeRedisScripts(Redis),\n        /* other custom user scripts defined here */\n      }\n    });\nconst limiter = RedisGCRA({ redis });\n\n(...)\n```\n\nIf you would like to otherwise customize the provided script definition, you can also import the LUA and customize the script definition further (for example, for usage with Typescript).\n\n```js\nconst Redis = require('redis');\nconst RedisGCRA = require('redis-gcra');\n\nconst redis = Redis.createClient({\n  scripts: {\n    performGcraRateLimit: Redis.defineScript({\n      NUMBER_OF_KEYS: 1,\n      SCRIPT: RedisGCRA.GCRA_LUA,\n      transformArguments(key, now, burst, rate, period, cost) {\n        return [key, now.toString(), burst.toString(), rate.toString(), period.toString(), cost.toString()];\n      },\n      transformReply(reply) {\n        return reply;\n      }\n    })\n  }\n});\n\nconst limiter = RedisGCRA({ redis });\n\n(...)\n```\n\n## Example\n\nIn this example the rate limit bucket has 1000 tokens, recovering at\na speed of 1 token per second.\n\n```js\nconst Redis     = require('ioredis');\nconst RedisGCRA = require('redis-gcra');\n\nconst redis = new Redis();\nconst limiter = RedisGCRA({ redis });\n\n// In order to perform rate limiting, you need to call the 'limit' method.\nlet result = await limiter.limit({\n  key: 'user/myUser@example.com',\n  burst: 1000,  // limit on maximum tokens available\n  rate: 1,      // rate at which tokens regenerate per period\n  period: 1000, // period, in milliseconds, for token regeneration\n  cost: 2       // cost in tokens for this limit request\n});\n\nresult.limited;   // => false - request should not be limited\nresult.remaining; // => 998   - remaining number of tokens until limited\nresult.retryIn;   // => 0     - can retry without delay\nresult.resetIn;   // => ~2000 - in approximately 2 seconds tokens will be regenerated to burst limit\n\n// call limit 500 more times in rapid succession and the 500th call will have:\n// result = await limiter.limit(....)\nresult.limited;   // => true    - request should be limited\nresult.remaining; // => 0       - remaining number of tokens until limited\nresult.retryIn;   // => 2000    - can retry in approximately 2 seconds\nresult.resetIn;   // => 1000000 - in approximately 1000 seconds tokens will be regenerated to burst limit\n```\n\nThe implementation utilizes a single key in Redis that matches the key you pass\nto the `limit` method. If you need to reset the rate limiter for a particular key,\ncall the reset method:\n\n```js\n// Let's imagine 'user/myUser@example.com' is limited.\n// This will effectively reset the limit for the key:\nawait limiter.reset({ key: 'user/myUser@example.com' })\n// limit is reset\n```\n\nYou can also retrieve the current state of the rate limiter for a particular key\nwithout actually modifying the state. In order to do that, use the `peek`\nmethod:\n\n```js\nlet result = await limiter.peek({\n  key: 'user/myUser@example.com',\n  burst: 1000,\n  rate: 1,\n  period: 1000\n});\n\nresult.limited;   // => false - request should not be limited\nresult.remaining; // => 1000  - remaining number of tokens until limited\nresult.resetIn;   // => 0     - \"burst\" tokens are already available\n```\n\n## Inspiration\n\nThis code was inspired by the Ruby gem [redis-gcra](https://github.com/rwz/redis-gcra).\n\n## License\n\nThe module is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","readmeFilename":"README.md"}