hono-rate-limiter 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +3 -4
- package/src/core/defaultKeyGenerator.js +1 -1
- package/src/core/headers.js +1 -1
- package/src/core/index.js +1 -1
- package/src/core/validations.js +1 -1
- package/src/index.js +1 -1
- package/src/memory-store/index.js +1 -1
- package/src/types/clientRateLimitInfo.js +1 -1
- package/src/types/config.js +1 -1
- package/src/types/index.js +1 -1
- package/src/types/promisify.js +1 -1
- package/src/types/rateLimitExceededEventHandler.js +1 -1
- package/src/types/rateLimitInfo.js +1 -1
- package/src/types/store.js +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ app.use(limiter);
|
|
|
34
34
|
|
|
35
35
|
# Data Stores
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
`hono-rate-limit` supports external data stores to synchronize hit counts across multiple processes and servers.
|
|
38
38
|
|
|
39
39
|
By default, `MemoryStore` is used. This one does not synchronize its state across instances. It’s simple to deploy, and often sufficient for basic abuse prevention, but will be inconnsistent across reboots or in deployments with multiple process or servers.
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ Here is a list of stores:
|
|
|
51
51
|
| [`cluster-memory-store`](https://npm.im/@express-rate-limit/cluster-memory-store) | A memory-store wrapper that shares state across all processes on a single server via the [node:cluster](https://nodejs.org/api/cluster.html) module. Does not share state across multiple servers. |
|
|
52
52
|
| [`precise-memory-rate-limit`](https://www.npm.im/precise-memory-rate-limit) | A memory store similar to the built-in one, except that it stores a distinct timestamp for each key. |
|
|
53
53
|
| [`typeorm-rate-limit-store`](https://www.npmjs.com/package/typeorm-rate-limit-store) | Supports a variety of databases via [TypeORM](https://typeorm.io/): MySQL, MariaDB, CockroachDB, SQLite, Microsoft SQL Server, Oracle, SAP Hana, and more. |
|
|
54
|
-
| [`@rlimit/storage`](
|
|
54
|
+
| [`@rlimit/storage`](https://www.npmjs.com/package/@rlimit/storage) | A distributed rlimit store, ideal for multi-regional deployments. |
|
|
55
55
|
|
|
56
56
|
Take a look at this [guide](https://express-rate-limit.mintlify.app/guides/creating-a-store) if you wish to create your own store.
|
|
57
57
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono-rate-limiter",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"type": "
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"type": "commonjs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"hono",
|
|
@@ -31,6 +31,5 @@
|
|
|
31
31
|
"hono": "^4.1.1"
|
|
32
32
|
},
|
|
33
33
|
"main": "./src/index.js",
|
|
34
|
-
"typings": "./src/index.d.ts"
|
|
35
|
-
"module": "./src/index.js"
|
|
34
|
+
"typings": "./src/index.d.ts"
|
|
36
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"defaultKeyGenerator",{enumerable:true,get:function(){return defaultKeyGenerator}});const _adapter=require("hono/adapter");function defaultKeyGenerator(c){const runtime=(0,_adapter.getRuntimeKey)();let key=null;switch(runtime){case"workerd":key=c.req.raw.headers.get("CF-Connecting-IP");break;case"fastly":key=c.req.raw.headers.get("Fastly-Client-IP");break;case"other":key=c.req.raw.headers.get("x-real-ip");break;default:break}return key??""}
|
package/src/core/headers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const getResetSeconds=(resetTime,windowMs)=>{let resetSeconds;if(resetTime){const deltaSeconds=Math.ceil((resetTime.getTime()-Date.now())/1e3);resetSeconds=Math.max(0,deltaSeconds)}else if(windowMs){resetSeconds=Math.ceil(windowMs/1e3)}return resetSeconds};
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:all[name]})}_export(exports,{setDraft6Headers:function(){return setDraft6Headers},setDraft7Headers:function(){return setDraft7Headers},setRetryAfterHeader:function(){return setRetryAfterHeader}});const getResetSeconds=(resetTime,windowMs)=>{let resetSeconds;if(resetTime){const deltaSeconds=Math.ceil((resetTime.getTime()-Date.now())/1e3);resetSeconds=Math.max(0,deltaSeconds)}else if(windowMs){resetSeconds=Math.ceil(windowMs/1e3)}return resetSeconds};const setDraft6Headers=(context,info,windowMs)=>{if(context.finalized)return;const windowSeconds=Math.ceil(windowMs/1e3);const resetSeconds=getResetSeconds(info.resetTime);context.header("RateLimit-Policy",`${info.limit};w=${windowSeconds}`);context.header("RateLimit-Limit",info.limit.toString());context.header("RateLimit-Remaining",info.remaining.toString());if(resetSeconds)context.header("RateLimit-Reset",resetSeconds.toString())};const setDraft7Headers=(context,info,windowMs)=>{if(context.finalized)return;const windowSeconds=Math.ceil(windowMs/1e3);const resetSeconds=getResetSeconds(info.resetTime,windowMs);context.header("RateLimit-Policy",`${info.limit};w=${windowSeconds}`);context.header("RateLimit",`limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`)};const setRetryAfterHeader=(context,info,windowMs)=>{if(context.finalized)return;const resetSeconds=getResetSeconds(info.resetTime,windowMs);context.header("Retry-After",resetSeconds?.toString())};
|
package/src/core/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"rateLimiter",{enumerable:true,get:function(){return rateLimiter}});const _interop_require_default=require("@swc/helpers/_/_interop_require_default");const _factory=require("hono/factory");const _memorystore=_interop_require_default._(require("../memory-store"));const _defaultKeyGenerator=require("./defaultKeyGenerator");const _headers=require("./headers");const _validations=require("./validations");function rateLimiter(config){const{windowMs=6e4,limit=5,message="Too many requests, please try again later.",statusCode=429,standardHeaders="draft-6",requestPropertyName="rateLimit",requestStorePropertyName="rateLimitStore",skipFailedRequests=false,skipSuccessfulRequests=false,keyGenerator=_defaultKeyGenerator.defaultKeyGenerator,skip=()=>false,requestWasSuccessful=c=>c.res.status<400,handler=async(c,_next,options)=>{c.status(options.statusCode);const responseMessage=typeof options.message==="function"?await options.message(c):options.message;if(typeof responseMessage==="string")return c.text(responseMessage);return c.json(responseMessage)},store=new _memorystore.default}=config??{};const options={windowMs,limit,message,statusCode,standardHeaders,requestPropertyName,requestStorePropertyName,skipFailedRequests,skipSuccessfulRequests,keyGenerator,skip,requestWasSuccessful,handler,store};if(!(0,_validations.isValidStore)(store))throw new Error("The store is not correctly implmented!");if(typeof store.init==="function")store.init(options);return(0,_factory.createMiddleware)(async(c,next)=>{const isSkippable=await skip(c);if(isSkippable){await next();return}const key=await keyGenerator(c);const{totalHits,resetTime}=await store.increment(key);const retrieveLimit=typeof limit==="function"?limit(c):limit;const _limit=await retrieveLimit;const info={limit:_limit,used:totalHits,remaining:Math.max(_limit-totalHits,0),resetTime};c.set(requestPropertyName,info);c.set(requestStorePropertyName,{getKey:store.get?.bind(store),resetKey:store.resetKey.bind(store)});if(standardHeaders&&!c.finalized){if(standardHeaders==="draft-7"){(0,_headers.setDraft7Headers)(c,info,windowMs)}else{(0,_headers.setDraft6Headers)(c,info,windowMs)}}let decremented=false;const decrementKey=async()=>{if(!decremented){await store.decrement(key);decremented=true}};const shouldSkipRequest=async()=>{if(skipFailedRequests||skipSuccessfulRequests){const wasRequestSuccessful=await requestWasSuccessful(c);if(skipFailedRequests&&!wasRequestSuccessful||skipSuccessfulRequests&&wasRequestSuccessful)await decrementKey()}};if(totalHits>_limit){if(standardHeaders){(0,_headers.setRetryAfterHeader)(c,info,windowMs)}await shouldSkipRequest();return handler(c,next,options)}try{await next();await shouldSkipRequest()}catch(error){if(skipFailedRequests)await decrementKey()}finally{if(!c.finalized)await decrementKey()}})}
|
package/src/core/validations.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"isValidStore",{enumerable:true,get:function(){return isValidStore}});const isValidStore=value=>!!value?.increment;
|
package/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:all[name]})}_export(exports,{default:function(){return _core.rateLimiter},rateLimiter:function(){return _core.rateLimiter}});const _core=require("./core");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:all[name]})}_export(exports,{MemoryStore:function(){return MemoryStore},default:function(){return _default}});let MemoryStore=class MemoryStore{init(options){this.windowMs=options.windowMs;if(this.interval)clearInterval(this.interval);this.interval=setInterval(()=>{this.clearExpired()},this.windowMs);if(this.interval.unref)this.interval.unref()}async get(key){return this.current.get(key)??this.previous.get(key)}async increment(key){const client=this.getClient(key);const now=Date.now();if(client.resetTime.getTime()<=now){this.resetClient(client,now)}client.totalHits++;return client}async decrement(key){const client=this.getClient(key);if(client.totalHits>0)client.totalHits--}async resetKey(key){this.current.delete(key);this.previous.delete(key)}async resetAll(){this.current.clear();this.previous.clear()}shutdown(){clearInterval(this.interval);void this.resetAll()}resetClient(client,now=Date.now()){client.totalHits=0;client.resetTime.setTime(now+this.windowMs);return client}getClient(key){if(this.current.has(key))return this.current.get(key);let client;if(this.previous.has(key)){client=this.previous.get(key);this.previous.delete(key)}else{client={totalHits:0,resetTime:new Date};this.resetClient(client)}this.current.set(key,client);return client}clearExpired(){this.previous=this.current;this.current=new Map}constructor(){this.previous=new Map;this.current=new Map;this.localKeys=true}};const _default=MemoryStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
package/src/types/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
package/src/types/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
package/src/types/promisify.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
package/src/types/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|