@joint-ops/hitlimit 1.0.6 → 1.1.0
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 +19 -1
- package/dist/hono.d.ts +6 -0
- package/dist/hono.d.ts.map +1 -0
- package/dist/hono.js +44 -0
- package/dist/hono.js.map +1 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- **Blazing Fast** - 2,450,000+ ops/sec with memory store (multi-IP scenarios), ~7% HTTP overhead
|
|
18
18
|
- **Zero Config** - Works out of the box with sensible defaults
|
|
19
19
|
- **Tiny Footprint** - Only ~7KB core, zero runtime dependencies
|
|
20
|
-
- **Framework Agnostic** - Express,
|
|
20
|
+
- **Framework Agnostic** - Express, Fastify, Hono, NestJS, native HTTP
|
|
21
21
|
- **Multiple Stores** - Memory, Redis, SQLite for distributed systems
|
|
22
22
|
- **TypeScript First** - Full type safety and IntelliSense support
|
|
23
23
|
- **Flexible Keys** - Rate limit by IP, user ID, API key, or custom logic
|
|
@@ -124,6 +124,24 @@ app.get('/api', () => ({ status: 'ok' }))
|
|
|
124
124
|
await app.listen({ port: 3000 })
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### Hono Rate Limiting
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { Hono } from 'hono'
|
|
131
|
+
import { serve } from '@hono/node-server'
|
|
132
|
+
import { hitlimit } from '@joint-ops/hitlimit/hono'
|
|
133
|
+
|
|
134
|
+
const app = new Hono()
|
|
135
|
+
|
|
136
|
+
app.use(hitlimit({
|
|
137
|
+
limit: 100,
|
|
138
|
+
window: '1m'
|
|
139
|
+
}))
|
|
140
|
+
|
|
141
|
+
app.get('/api', (c) => c.json({ status: 'ok' }))
|
|
142
|
+
serve({ fetch: app.fetch, port: 3000 })
|
|
143
|
+
```
|
|
144
|
+
|
|
127
145
|
### NestJS Rate Limiting
|
|
128
146
|
|
|
129
147
|
```typescript
|
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { HitLimitOptions } from '@joint-ops/hitlimit-types';
|
|
3
|
+
export declare function hitlimit(options?: HitLimitOptions<Context>): import("hono").MiddlewareHandler<any, string, {}, Response | (Response & import("hono").TypedResponse<{
|
|
4
|
+
[x: string]: any;
|
|
5
|
+
}, 429, "json">)>;
|
|
6
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAahE,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAe,CAAC,OAAO,CAAM;;kBAmC9D"}
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createMiddleware } from 'hono/factory';
|
|
2
|
+
import { resolveConfig } from './core/config.js';
|
|
3
|
+
import { checkLimit } from './core/limiter.js';
|
|
4
|
+
import { memoryStore } from './stores/memory.js';
|
|
5
|
+
function getDefaultKey(c) {
|
|
6
|
+
// Hono doesn't expose raw IP — it's runtime-dependent
|
|
7
|
+
// Use standard proxy headers as fallback
|
|
8
|
+
return c.req.header('x-forwarded-for')?.split(',')[0]?.trim()
|
|
9
|
+
|| c.req.header('x-real-ip')
|
|
10
|
+
|| 'unknown';
|
|
11
|
+
}
|
|
12
|
+
export function hitlimit(options = {}) {
|
|
13
|
+
const store = options.store ?? memoryStore();
|
|
14
|
+
const config = resolveConfig(options, store, getDefaultKey);
|
|
15
|
+
return createMiddleware(async (c, next) => {
|
|
16
|
+
// Skip check
|
|
17
|
+
if (config.skip) {
|
|
18
|
+
const shouldSkip = await config.skip(c);
|
|
19
|
+
if (shouldSkip) {
|
|
20
|
+
await next();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const result = await checkLimit(config, c);
|
|
26
|
+
// Set all rate limit headers
|
|
27
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
28
|
+
c.header(key, value);
|
|
29
|
+
}
|
|
30
|
+
// Block if not allowed
|
|
31
|
+
if (!result.allowed) {
|
|
32
|
+
return c.json(result.body, 429);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const action = await config.onStoreError(error, c);
|
|
37
|
+
if (action === 'deny') {
|
|
38
|
+
return c.json({ hitlimit: true, message: 'Rate limit error' }, 429);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await next();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=hono.js.map
|
package/dist/hono.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,SAAS,aAAa,CAAC,CAAU;IAC/B,sDAAsD;IACtD,yCAAyC;IACzC,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;WACxD,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;WACzB,SAAS,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAAoC,EAAE;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,EAAE,CAAA;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;IAE3D,OAAO,gBAAgB,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACxC,aAAa;QACb,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACvC,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,IAAI,EAAE,CAAA;gBACZ,OAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;YAE1C,6BAA6B;YAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YACtB,CAAC;YAED,uBAAuB;YACvB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;YACjC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAc,EAAE,CAAC,CAAC,CAAA;YAC3D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,GAAG,CAAC,CAAA;YACrE,CAAC;QACH,CAAC;QAED,MAAM,IAAI,EAAE,CAAA;IACd,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joint-ops/hitlimit",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Fast rate limiting middleware for Express, Fastify, NestJS & Node.js - API throttling, brute force protection, request limiting",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Shayan M Hussain",
|
|
@@ -93,6 +93,10 @@
|
|
|
93
93
|
"types": "./dist/fastify.d.ts",
|
|
94
94
|
"import": "./dist/fastify.js"
|
|
95
95
|
},
|
|
96
|
+
"./hono": {
|
|
97
|
+
"types": "./dist/hono.d.ts",
|
|
98
|
+
"import": "./dist/hono.js"
|
|
99
|
+
},
|
|
96
100
|
"./stores/memory": {
|
|
97
101
|
"types": "./dist/stores/memory.d.ts",
|
|
98
102
|
"import": "./dist/stores/memory.js"
|
|
@@ -129,14 +133,15 @@
|
|
|
129
133
|
"test:watch": "vitest"
|
|
130
134
|
},
|
|
131
135
|
"dependencies": {
|
|
132
|
-
"@joint-ops/hitlimit-types": "1.0
|
|
136
|
+
"@joint-ops/hitlimit-types": "1.1.0"
|
|
133
137
|
},
|
|
134
138
|
"peerDependencies": {
|
|
135
139
|
"@nestjs/common": ">=8.0.0",
|
|
136
140
|
"@nestjs/core": ">=8.0.0",
|
|
141
|
+
"better-sqlite3": ">=9.0.0",
|
|
137
142
|
"fastify": ">=4.0.0",
|
|
138
143
|
"fastify-plugin": ">=4.0.0",
|
|
139
|
-
"
|
|
144
|
+
"hono": ">=4.0.0",
|
|
140
145
|
"ioredis": ">=5.0.0",
|
|
141
146
|
"pino": ">=8.0.0",
|
|
142
147
|
"winston": ">=3.0.0"
|
|
@@ -154,6 +159,9 @@
|
|
|
154
159
|
"fastify-plugin": {
|
|
155
160
|
"optional": true
|
|
156
161
|
},
|
|
162
|
+
"hono": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
157
165
|
"better-sqlite3": {
|
|
158
166
|
"optional": true
|
|
159
167
|
},
|
|
@@ -168,6 +176,7 @@
|
|
|
168
176
|
}
|
|
169
177
|
},
|
|
170
178
|
"devDependencies": {
|
|
179
|
+
"@hono/node-server": "^1.19.9",
|
|
171
180
|
"@nestjs/common": "^10.0.0",
|
|
172
181
|
"@nestjs/core": "^10.0.0",
|
|
173
182
|
"@nestjs/platform-express": "^10.0.0",
|
|
@@ -180,6 +189,7 @@
|
|
|
180
189
|
"express": "^4.18.0",
|
|
181
190
|
"fastify": "^5.7.4",
|
|
182
191
|
"fastify-plugin": "^5.1.0",
|
|
192
|
+
"hono": "^4.11.9",
|
|
183
193
|
"ioredis": "^5.3.0",
|
|
184
194
|
"pino": "^10.3.0",
|
|
185
195
|
"reflect-metadata": "^0.2.0",
|