@joint-ops/hitlimit-bun 1.0.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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +399 -0
  3. package/package.json +120 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 JointOps
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,399 @@
1
+ # @joint-ops/hitlimit-bun
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@joint-ops/hitlimit-bun.svg)](https://www.npmjs.com/package/@joint-ops/hitlimit-bun)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@joint-ops/hitlimit-bun.svg)](https://www.npmjs.com/package/@joint-ops/hitlimit-bun)
5
+ [![GitHub](https://img.shields.io/github/license/JointOps/hitlimit-monorepo)](https://github.com/JointOps/hitlimit-monorepo)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
+ [![Bun](https://img.shields.io/badge/Bun-Native-black.svg)](https://bun.sh)
8
+
9
+ > The fastest rate limiter for Bun - Native bun:sqlite performance | Elysia rate limit plugin
10
+
11
+ **hitlimit-bun** is a high-performance, Bun-native rate limiting library for Bun.serve and Elysia applications. Built specifically for Bun's runtime with native bun:sqlite for maximum performance. The only rate limiter designed from the ground up for Bun.
12
+
13
+ **[Documentation](https://hitlimit.dev/docs/bun)** | **[GitHub](https://github.com/JointOps/hitlimit-monorepo)** | **[npm](https://www.npmjs.com/package/@joint-ops/hitlimit-bun)**
14
+
15
+ ## ⚡ Why hitlimit-bun?
16
+
17
+ **hitlimit-bun uses Bun's native SQLite** - no FFI overhead, no Node.js polyfills.
18
+
19
+ ```
20
+ ┌────────────────────────────────────────────────────────────────┐
21
+ │ │
22
+ │ bun:sqlite ████████████████████████████ 95,000 ops/s │
23
+ │ better-sqlite3 ██████████░░░░░░░░░░░░░░░░░░ 35,000 ops/s │
24
+ │ │
25
+ │ bun:sqlite is 2.7x faster because it's truly native │
26
+ │ │
27
+ └────────────────────────────────────────────────────────────────┘
28
+ ```
29
+
30
+ - **Bun Native** - Built specifically for Bun's runtime, not a Node.js port
31
+ - **2.7x Faster SQLite** - Native bun:sqlite vs Node.js better-sqlite3
32
+ - **95,000+ ops/sec** - With bun:sqlite persistence
33
+ - **Zero Config** - Works out of the box with sensible defaults
34
+ - **Elysia Plugin** - First-class Elysia framework integration
35
+ - **TypeScript First** - Full type safety and IntelliSense support
36
+ - **Tiny Footprint** - Only ~5KB minified, no bloat
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ bun add @joint-ops/hitlimit-bun
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ### Bun.serve Rate Limiting
47
+
48
+ ```typescript
49
+ import { hitlimit } from '@joint-ops/hitlimit-bun'
50
+
51
+ Bun.serve({
52
+ fetch: hitlimit({}, (req) => new Response('Hello!'))
53
+ })
54
+ ```
55
+
56
+ ### Elysia Rate Limiting
57
+
58
+ ```typescript
59
+ import { Elysia } from 'elysia'
60
+ import { hitlimit } from '@joint-ops/hitlimit-bun/elysia'
61
+
62
+ new Elysia()
63
+ .use(hitlimit({ limit: 100, window: '1m' }))
64
+ .get('/', () => 'Hello World!')
65
+ .listen(3000)
66
+ ```
67
+
68
+ ### Using createHitLimit
69
+
70
+ ```typescript
71
+ import { createHitLimit } from '@joint-ops/hitlimit-bun'
72
+
73
+ const limiter = createHitLimit({ limit: 100, window: '1m' })
74
+
75
+ Bun.serve({
76
+ async fetch(req) {
77
+ const result = await limiter(req)
78
+ if (!result.allowed) {
79
+ return new Response(JSON.stringify(result.body), {
80
+ status: 429,
81
+ headers: result.headers
82
+ })
83
+ }
84
+ return new Response('Hello!')
85
+ }
86
+ })
87
+ ```
88
+
89
+ ## Features
90
+
91
+ ### API Rate Limiting
92
+
93
+ Protect your Bun APIs from abuse with high-performance rate limiting.
94
+
95
+ ```typescript
96
+ Bun.serve({
97
+ fetch: hitlimit({ limit: 1000, window: '1h' }, handler)
98
+ })
99
+ ```
100
+
101
+ ### Login & Authentication Protection
102
+
103
+ Prevent brute force attacks on login endpoints.
104
+
105
+ ```typescript
106
+ const authLimiter = createHitLimit({ limit: 5, window: '15m' })
107
+
108
+ Bun.serve({
109
+ async fetch(req) {
110
+ const url = new URL(req.url)
111
+
112
+ if (url.pathname.startsWith('/auth')) {
113
+ const result = await authLimiter(req)
114
+ if (!result.allowed) {
115
+ return new Response('Too many attempts', { status: 429 })
116
+ }
117
+ }
118
+
119
+ return handler(req)
120
+ }
121
+ })
122
+ ```
123
+
124
+ ### Tiered Rate Limits
125
+
126
+ Different limits for different user tiers (free, pro, enterprise).
127
+
128
+ ```typescript
129
+ hitlimit({
130
+ tiers: {
131
+ free: { limit: 100, window: '1h' },
132
+ pro: { limit: 5000, window: '1h' },
133
+ enterprise: { limit: Infinity }
134
+ },
135
+ tier: (req) => req.headers.get('x-tier') || 'free'
136
+ }, handler)
137
+ ```
138
+
139
+ ### Custom Rate Limit Keys
140
+
141
+ Rate limit by IP address, user ID, API key, or any custom identifier.
142
+
143
+ ```typescript
144
+ hitlimit({
145
+ key: (req) => req.headers.get('x-api-key') || 'anonymous'
146
+ }, handler)
147
+ ```
148
+
149
+ ### Elysia Route-Specific Limits
150
+
151
+ Apply different limits to different route groups in Elysia.
152
+
153
+ ```typescript
154
+ new Elysia()
155
+ // Global limit
156
+ .use(hitlimit({ limit: 100, window: '1m' }))
157
+
158
+ // Stricter limit for auth
159
+ .group('/auth', (app) =>
160
+ app
161
+ .use(hitlimit({ limit: 5, window: '15m' }))
162
+ .post('/login', handler)
163
+ )
164
+
165
+ // Higher limit for API
166
+ .group('/api', (app) =>
167
+ app
168
+ .use(hitlimit({ limit: 1000, window: '1m' }))
169
+ .get('/data', handler)
170
+ )
171
+ .listen(3000)
172
+ ```
173
+
174
+ ## Configuration Options
175
+
176
+ ```typescript
177
+ hitlimit({
178
+ // Basic options
179
+ limit: 100, // Max requests per window (default: 100)
180
+ window: '1m', // Time window: 30s, 15m, 1h, 1d (default: '1m')
181
+
182
+ // Custom key extraction
183
+ key: (req) => req.headers.get('x-api-key') || 'anonymous',
184
+
185
+ // Tiered rate limits
186
+ tiers: {
187
+ free: { limit: 100, window: '1h' },
188
+ pro: { limit: 5000, window: '1h' },
189
+ enterprise: { limit: Infinity }
190
+ },
191
+ tier: (req) => req.headers.get('x-tier') || 'free',
192
+
193
+ // Custom 429 response
194
+ response: {
195
+ message: 'Too many requests',
196
+ statusCode: 429
197
+ },
198
+ // Or function:
199
+ response: (info) => ({
200
+ error: 'RATE_LIMITED',
201
+ retryIn: info.resetIn
202
+ }),
203
+
204
+ // Headers configuration
205
+ headers: {
206
+ standard: true, // RateLimit-* headers
207
+ legacy: true, // X-RateLimit-* headers
208
+ retryAfter: true // Retry-After header on 429
209
+ },
210
+
211
+ // Store (default: bun:sqlite)
212
+ store: sqliteStore({ path: './ratelimit.db' }),
213
+
214
+ // Skip rate limiting
215
+ skip: (req) => req.url.includes('/health'),
216
+
217
+ // Error handling
218
+ onStoreError: (error, req) => {
219
+ console.error('Store error:', error)
220
+ return 'allow' // or 'deny'
221
+ }
222
+ }, handler)
223
+ ```
224
+
225
+ ## Storage Backends
226
+
227
+ ### SQLite Store (Default)
228
+
229
+ Uses Bun's native bun:sqlite for maximum performance. Default store.
230
+
231
+ ```typescript
232
+ import { hitlimit } from '@joint-ops/hitlimit-bun'
233
+
234
+ // Default - uses bun:sqlite with in-memory database
235
+ Bun.serve({
236
+ fetch: hitlimit({}, handler)
237
+ })
238
+
239
+ // Custom path for persistence
240
+ import { sqliteStore } from '@joint-ops/hitlimit-bun'
241
+
242
+ Bun.serve({
243
+ fetch: hitlimit({
244
+ store: sqliteStore({ path: './ratelimit.db' })
245
+ }, handler)
246
+ })
247
+ ```
248
+
249
+ ### Memory Store
250
+
251
+ For simple use cases without persistence.
252
+
253
+ ```typescript
254
+ import { hitlimit } from '@joint-ops/hitlimit-bun'
255
+ import { memoryStore } from '@joint-ops/hitlimit-bun/stores/memory'
256
+
257
+ Bun.serve({
258
+ fetch: hitlimit({
259
+ store: memoryStore()
260
+ }, handler)
261
+ })
262
+ ```
263
+
264
+ ### Redis Store
265
+
266
+ For distributed systems and multi-server deployments.
267
+
268
+ ```typescript
269
+ import { hitlimit } from '@joint-ops/hitlimit-bun'
270
+ import { redisStore } from '@joint-ops/hitlimit-bun/stores/redis'
271
+
272
+ Bun.serve({
273
+ fetch: hitlimit({
274
+ store: redisStore({ url: 'redis://localhost:6379' })
275
+ }, handler)
276
+ })
277
+ ```
278
+
279
+ ## Response Headers
280
+
281
+ Every response includes rate limit information:
282
+
283
+ ```
284
+ RateLimit-Limit: 100
285
+ RateLimit-Remaining: 99
286
+ RateLimit-Reset: 1234567890
287
+ X-RateLimit-Limit: 100
288
+ X-RateLimit-Remaining: 99
289
+ X-RateLimit-Reset: 1234567890
290
+ ```
291
+
292
+ When rate limited (429 Too Many Requests):
293
+
294
+ ```
295
+ Retry-After: 42
296
+ ```
297
+
298
+ ## Default 429 Response
299
+
300
+ ```json
301
+ {
302
+ "hitlimit": true,
303
+ "message": "Whoa there! Rate limit exceeded.",
304
+ "limit": 100,
305
+ "remaining": 0,
306
+ "resetIn": 42
307
+ }
308
+ ```
309
+
310
+ ## Performance
311
+
312
+ hitlimit-bun is optimized for Bun's runtime with native performance:
313
+
314
+ ### Store Benchmarks (Bun 1.0)
315
+
316
+ | Store | Operations/sec | vs Node.js |
317
+ |-------|----------------|------------|
318
+ | **Memory** | 500,000+ | +25% faster |
319
+ | **bun:sqlite** | 95,000+ | **+171% faster** 🔥 |
320
+ | **Redis** | 15,000+ | +25% faster |
321
+
322
+ ### HTTP Throughput
323
+
324
+ | Framework | With hitlimit-bun | Overhead |
325
+ |-----------|-------------------|----------|
326
+ | **Bun.serve** | 105,000 req/s | 12% |
327
+ | **Elysia** | 115,000 req/s | 11% |
328
+
329
+ ### Why bun:sqlite is So Fast
330
+
331
+ ```
332
+ Node.js (better-sqlite3) Bun (bun:sqlite)
333
+ ───────────────────────── ─────────────────
334
+ JavaScript JavaScript
335
+ ↓ ↓
336
+ N-API Direct Call
337
+ ↓ ↓
338
+ C++ Binding Native SQLite
339
+ ↓ (No overhead!)
340
+ SQLite
341
+ ```
342
+
343
+ better-sqlite3 uses N-API bindings with C++ overhead.
344
+ bun:sqlite calls SQLite directly from Bun's native layer.
345
+
346
+ <details>
347
+ <summary>Run benchmarks yourself</summary>
348
+
349
+ ```bash
350
+ git clone https://github.com/JointOps/hitlimit-monorepo
351
+ cd hitlimit-monorepo
352
+ bun install
353
+ bun run benchmark:bun
354
+ ```
355
+
356
+ </details>
357
+
358
+ ## Elysia Plugin Options
359
+
360
+ ```typescript
361
+ import { Elysia } from 'elysia'
362
+ import { hitlimit } from '@joint-ops/hitlimit-bun/elysia'
363
+
364
+ new Elysia()
365
+ .use(hitlimit({
366
+ limit: 100,
367
+ window: '1m',
368
+ key: ({ request }) => request.headers.get('x-api-key') || 'anonymous',
369
+ tiers: {
370
+ free: { limit: 100, window: '1h' },
371
+ pro: { limit: 5000, window: '1h' }
372
+ },
373
+ tier: ({ request }) => request.headers.get('x-tier') || 'free'
374
+ }))
375
+ .get('/', () => 'Hello!')
376
+ .listen(3000)
377
+ ```
378
+
379
+ ## Related Packages
380
+
381
+ - [@joint-ops/hitlimit](https://www.npmjs.com/package/@joint-ops/hitlimit) - Node.js rate limiting for Express, NestJS
382
+
383
+ ## Why Not Use Node.js Rate Limiters in Bun?
384
+
385
+ Node.js rate limiters like express-rate-limit use better-sqlite3 which relies on N-API bindings. In Bun, this adds overhead and loses the performance benefits of Bun's native runtime.
386
+
387
+ **hitlimit-bun** is built specifically for Bun:
388
+ - Uses native `bun:sqlite` (2.7x faster than better-sqlite3)
389
+ - No FFI overhead or Node.js polyfills
390
+ - First-class Elysia framework support
391
+ - Optimized for Bun.serve's request handling
392
+
393
+ ## License
394
+
395
+ MIT - Use freely in personal and commercial projects.
396
+
397
+ ## Keywords
398
+
399
+ bun rate limit, bun rate limiter, bun middleware, bun api, bun server, bun serve, bun framework, bun native, bun sqlite, elysia rate limit, elysia plugin, elysia middleware, elysia throttle, elysia framework, api rate limiting, throttle requests, request throttling, bun api protection, ddos protection, brute force protection, login protection, redis rate limit, high performance rate limit, fast rate limiter, sliding window, fixed window, rate-limiter-flexible bun, express-rate-limit bun, bun http, bun backend, bun rest api
package/package.json ADDED
@@ -0,0 +1,120 @@
1
+ {
2
+ "name": "@joint-ops/hitlimit-bun",
3
+ "version": "1.0.0",
4
+ "description": "Fast Bun-native rate limiting for Bun.serve & Elysia - API throttling with bun:sqlite, high performance request limiting",
5
+ "author": {
6
+ "name": "Shayan M Hussain",
7
+ "email": "shayanhussain48@gmail.com",
8
+ "url": "https://github.com/ShayanHussainSB"
9
+ },
10
+ "license": "MIT",
11
+ "homepage": "https://hitlimit.dev/docs/bun",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/JointOps/hitlimit-monorepo",
15
+ "directory": "packages/hitlimit-bun"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/JointOps/hitlimit-monorepo/issues"
19
+ },
20
+ "keywords": [
21
+ "rate-limit",
22
+ "rate-limiter",
23
+ "rate-limiting",
24
+ "bun",
25
+ "bun-rate-limit",
26
+ "bun-sqlite",
27
+ "bun-middleware",
28
+ "bun-serve",
29
+ "bun-server",
30
+ "bun-native",
31
+ "elysia",
32
+ "elysia-plugin",
33
+ "elysia-rate-limit",
34
+ "elysia-middleware",
35
+ "elysia-throttle",
36
+ "middleware",
37
+ "throttle",
38
+ "throttling",
39
+ "api",
40
+ "api-rate-limit",
41
+ "api-throttle",
42
+ "request-limit",
43
+ "request-throttling",
44
+ "ddos-protection",
45
+ "brute-force",
46
+ "brute-force-protection",
47
+ "login-protection",
48
+ "redis",
49
+ "redis-rate-limit",
50
+ "typescript",
51
+ "high-performance",
52
+ "fast-rate-limiter",
53
+ "sliding-window",
54
+ "fixed-window",
55
+ "rate-limiter-flexible",
56
+ "api-security",
57
+ "request-limiter",
58
+ "http-rate-limit",
59
+ "bun-api",
60
+ "bun-framework"
61
+ ],
62
+ "type": "module",
63
+ "main": "./dist/index.js",
64
+ "module": "./dist/index.js",
65
+ "types": "./dist/index.d.ts",
66
+ "exports": {
67
+ ".": {
68
+ "types": "./dist/index.d.ts",
69
+ "bun": "./dist/index.js",
70
+ "import": "./dist/index.js"
71
+ },
72
+ "./elysia": {
73
+ "types": "./dist/elysia.d.ts",
74
+ "bun": "./dist/elysia.js",
75
+ "import": "./dist/elysia.js"
76
+ },
77
+ "./stores/memory": {
78
+ "types": "./dist/stores/memory.d.ts",
79
+ "bun": "./dist/stores/memory.js",
80
+ "import": "./dist/stores/memory.js"
81
+ },
82
+ "./stores/redis": {
83
+ "types": "./dist/stores/redis.d.ts",
84
+ "bun": "./dist/stores/redis.js",
85
+ "import": "./dist/stores/redis.js"
86
+ }
87
+ },
88
+ "files": [
89
+ "dist"
90
+ ],
91
+ "sideEffects": false,
92
+ "scripts": {
93
+ "build": "bun build ./src/index.ts ./src/elysia.ts ./src/stores/memory.ts ./src/stores/redis.ts --outdir=./dist --target=bun && tsc --emitDeclarationOnly",
94
+ "clean": "rm -rf dist",
95
+ "test": "bun test",
96
+ "test:watch": "bun test --watch"
97
+ },
98
+ "dependencies": {
99
+ "@joint-ops/hitlimit-types": "1.0.0"
100
+ },
101
+ "peerDependencies": {
102
+ "elysia": ">=1.0.0",
103
+ "ioredis": ">=5.0.0"
104
+ },
105
+ "peerDependenciesMeta": {
106
+ "elysia": {
107
+ "optional": true
108
+ },
109
+ "ioredis": {
110
+ "optional": true
111
+ }
112
+ },
113
+ "devDependencies": {
114
+ "@sinclair/typebox": "^0.34.48",
115
+ "@types/bun": "latest",
116
+ "elysia": "^1.0.0",
117
+ "ioredis": "^5.3.0",
118
+ "typescript": "^5.3.0"
119
+ }
120
+ }