@periodic/titanium 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 [Uday Thakur]
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,608 @@
1
+ # 🛡️ Periodic Titanium
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@periodic/titanium.svg)](https://www.npmjs.com/package/@periodic/titanium)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue)](https://www.typescriptlang.org/)
6
+
7
+ Production-ready Redis-backed rate limiting middleware for Express with TypeScript support, fail-safe design, and flexible configuration.
8
+
9
+ ## 🎯 Why Titanium?
10
+
11
+ Building a robust API requires protecting your endpoints from abuse, but most rate limiting solutions come with significant tradeoffs:
12
+
13
+ - **In-memory limiters** don't scale across multiple servers
14
+ - **Generic packages** force you into opinionated implementations
15
+ - **Complex solutions** add unnecessary overhead for simple use cases
16
+
17
+ **PERIODIC Titanium** provides the perfect middle ground:
18
+
19
+ ✅ **Redis-backed** for distributed rate limiting across multiple instances
20
+ ✅ **Framework-agnostic core** with clean Express adapter
21
+ ✅ **TypeScript-first** with complete type safety
22
+ ✅ **Fail-safe design** that never breaks your application
23
+ ✅ **Zero dependencies** except Express and Redis (peer dependencies)
24
+ ✅ **Flexible configuration** for user-based, IP-based, or custom identification
25
+ ✅ **Production-tested** with atomic Redis operations to prevent race conditions
26
+
27
+ ---
28
+
29
+ ## 📦 Installation
30
+
31
+ ```bash
32
+ npm install @periodic/titanium redis express
33
+ ```
34
+
35
+ **Peer Dependencies:**
36
+ - `express` ^4.0.0 || ^5.0.0
37
+ - `redis` ^4.0.0
38
+
39
+ ---
40
+
41
+ ## 🚀 Quick Start
42
+
43
+ ### Basic Usage (IP-based)
44
+
45
+ ```typescript
46
+ import express from 'express';
47
+ import { createClient } from 'redis';
48
+ import { rateLimit } from '@periodic/titanium';
49
+
50
+ const app = express();
51
+
52
+ // Create and connect Redis client
53
+ const redis = createClient({
54
+ url: process.env.REDIS_URL || 'redis://localhost:6379'
55
+ });
56
+ await redis.connect();
57
+
58
+ // Apply rate limiting to all routes
59
+ app.use(rateLimit({
60
+ redis,
61
+ limit: 100, // 100 requests
62
+ window: 60, // per 60 seconds
63
+ keyPrefix: 'api' // Redis key prefix
64
+ }));
65
+
66
+ app.get('/api/data', (req, res) => {
67
+ res.json({ message: 'Success!' });
68
+ });
69
+
70
+ app.listen(3000);
71
+ ```
72
+
73
+ ### User-based Rate Limiting (JWT)
74
+
75
+ ```typescript
76
+ import { rateLimit } from '@periodic/titanium';
77
+
78
+ // Rate limit based on authenticated user ID
79
+ app.post('/api/resource',
80
+ authMiddleware, // Your JWT auth middleware
81
+ rateLimit({
82
+ redis,
83
+ limit: 10,
84
+ window: 60,
85
+ keyPrefix: 'create-resource',
86
+ // Extract user ID from JWT token
87
+ identifier: (req) => req.user?.id?.toString() || null
88
+ }),
89
+ createResourceHandler
90
+ );
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 🎛️ Configuration Options
96
+
97
+ ### Core Options
98
+
99
+ | Option | Type | Required | Default | Description |
100
+ |--------|------|----------|---------|-------------|
101
+ | `redis` | `RedisClientType` | ✅ Yes | - | Connected Redis client instance |
102
+ | `limit` | `number` | ✅ Yes | - | Maximum requests allowed in time window |
103
+ | `window` | `number` | ✅ Yes | - | Time window in seconds |
104
+ | `keyPrefix` | `string` | ✅ Yes | - | Redis key prefix (identifies rate limit type) |
105
+ | `algorithm` | `'fixed-window'` | No | `'fixed-window'` | Rate limiting algorithm |
106
+ | `logger` | `Logger` | No | `console` | Custom logger instance |
107
+
108
+ ### Express-Specific Options
109
+
110
+ | Option | Type | Default | Description |
111
+ |--------|------|---------|-------------|
112
+ | `identifier` | `(req) => string \| null` | IP-based | Custom function to extract client identifier |
113
+ | `message` | `string` | `'Too many requests...'` | Error message when limit exceeded |
114
+ | `skip` | `(req) => boolean` | - | Function to skip rate limiting conditionally |
115
+ | `failStrategy` | `'open' \| 'closed'` | `'open'` | Behavior when Redis is unavailable |
116
+ | `standardHeaders` | `boolean` | `true` | Include standard rate limit headers |
117
+
118
+ ---
119
+
120
+ ## 📚 Common Patterns
121
+
122
+ ### 1. Different Limits per Route
123
+
124
+ ```typescript
125
+ // Strict limit for authentication
126
+ app.post('/api/login',
127
+ rateLimit({
128
+ redis,
129
+ limit: 5,
130
+ window: 300, // 5 requests per 5 minutes
131
+ keyPrefix: 'login',
132
+ message: 'Too many login attempts. Try again in 5 minutes.'
133
+ }),
134
+ loginHandler
135
+ );
136
+
137
+ // Moderate limit for API mutations
138
+ app.post('/api/posts',
139
+ authMiddleware,
140
+ rateLimit({
141
+ redis,
142
+ limit: 20,
143
+ window: 60, // 20 requests per minute
144
+ keyPrefix: 'create-post',
145
+ identifier: (req) => req.user?.id?.toString() || null
146
+ }),
147
+ createPostHandler
148
+ );
149
+
150
+ // Lenient limit for reads
151
+ app.get('/api/posts',
152
+ rateLimit({
153
+ redis,
154
+ limit: 100,
155
+ window: 60, // 100 requests per minute
156
+ keyPrefix: 'list-posts'
157
+ }),
158
+ listPostsHandler
159
+ );
160
+ ```
161
+
162
+ ### 2. API Key-based Rate Limiting
163
+
164
+ ```typescript
165
+ app.use('/api', rateLimit({
166
+ redis,
167
+ limit: 1000,
168
+ window: 3600, // 1000 requests per hour
169
+ keyPrefix: 'api-key',
170
+ identifier: (req) => {
171
+ const apiKey = req.headers['x-api-key'] as string;
172
+ return apiKey || null; // Falls back to IP if no API key
173
+ }
174
+ }));
175
+ ```
176
+
177
+ ### 3. Tiered Rate Limits (Free vs Premium)
178
+
179
+ ```typescript
180
+ app.use('/api', authMiddleware, (req, res, next) => {
181
+ const limiter = req.user?.isPremium
182
+ ? rateLimit({
183
+ redis,
184
+ limit: 10000,
185
+ window: 3600,
186
+ keyPrefix: 'premium'
187
+ })
188
+ : rateLimit({
189
+ redis,
190
+ limit: 100,
191
+ window: 3600,
192
+ keyPrefix: 'free'
193
+ });
194
+
195
+ return limiter(req, res, next);
196
+ });
197
+ ```
198
+
199
+ ### 4. Skip Rate Limiting for Admins
200
+
201
+ ```typescript
202
+ app.use('/api/admin', rateLimit({
203
+ redis,
204
+ limit: 100,
205
+ window: 60,
206
+ keyPrefix: 'admin',
207
+ skip: (req) => req.user?.role === 'admin' // Admins bypass rate limit
208
+ }));
209
+ ```
210
+
211
+ ### 5. Cascading Rate Limits (Global + Route-specific)
212
+
213
+ ```typescript
214
+ // Global safety net
215
+ app.use('/api', rateLimit({
216
+ redis,
217
+ limit: 1000,
218
+ window: 3600,
219
+ keyPrefix: 'global'
220
+ }));
221
+
222
+ // Stricter limit on expensive operations
223
+ app.post('/api/ai/generate', rateLimit({
224
+ redis,
225
+ limit: 3,
226
+ window: 3600,
227
+ keyPrefix: 'ai-generation'
228
+ }), generateHandler);
229
+ ```
230
+
231
+ ---
232
+
233
+ ## 🔒 Fail Strategies
234
+
235
+ ### Fail-Open (Default, Recommended)
236
+
237
+ When Redis is unavailable, **allow requests through**.
238
+
239
+ ```typescript
240
+ rateLimit({
241
+ redis,
242
+ limit: 100,
243
+ window: 60,
244
+ keyPrefix: 'api',
245
+ failStrategy: 'open' // Default
246
+ });
247
+ ```
248
+
249
+ **Best for:** High-availability applications where downtime is unacceptable.
250
+
251
+ ### Fail-Closed (Strict Security)
252
+
253
+ When Redis is unavailable, **block all requests**.
254
+
255
+ ```typescript
256
+ rateLimit({
257
+ redis,
258
+ limit: 100,
259
+ window: 60,
260
+ keyPrefix: 'api',
261
+ failStrategy: 'closed'
262
+ });
263
+ ```
264
+
265
+ **Best for:** Security-critical endpoints where rate limiting must be enforced.
266
+
267
+ ---
268
+
269
+ ## 🏗️ Rate Limiting Algorithm
270
+
271
+ ### Fixed Window (Current Implementation)
272
+
273
+ The package uses a **fixed window** algorithm:
274
+
275
+ - Time is divided into fixed windows (e.g., 0-60s, 60-120s, etc.)
276
+ - Each window has an independent counter
277
+ - Counter resets at window boundaries
278
+
279
+ **Implementation:**
280
+ ```
281
+ SET key 0 EX window NX // Initialize window if not exists
282
+ INCR key // Increment counter atomically
283
+ ```
284
+
285
+ **Characteristics:**
286
+ - ✅ Simple and efficient
287
+ - ✅ Predictable behavior
288
+ - ✅ Low memory usage (one key per identifier)
289
+ - ⚠️ Potential for bursts at window boundaries (e.g., 100 requests at 59s, 100 more at 61s)
290
+
291
+ **Why not sliding window?**
292
+ - Sliding window requires storing individual request timestamps (higher memory)
293
+ - Fixed window is sufficient for 99% of use cases
294
+ - Can be mitigated with shorter windows if needed
295
+
296
+ ---
297
+
298
+ ## 📊 Response Headers
299
+
300
+ When `standardHeaders: true` (default), the middleware adds these headers:
301
+
302
+ ### Success Response
303
+ ```
304
+ X-RateLimit-Limit: 100
305
+ X-RateLimit-Remaining: 73
306
+ X-RateLimit-Reset: 1640995200
307
+ ```
308
+
309
+ ### Rate Limit Exceeded (429)
310
+ ```
311
+ HTTP/1.1 429 Too Many Requests
312
+ X-RateLimit-Limit: 100
313
+ X-RateLimit-Remaining: 0
314
+ X-RateLimit-Reset: 1640995200
315
+ Retry-After: 45
316
+
317
+ {
318
+ "error": "Too many requests. Please try again later.",
319
+ "retryAfter": 45,
320
+ "limit": 100,
321
+ "remaining": 0,
322
+ "reset": 1640995200
323
+ }
324
+ ```
325
+
326
+ ---
327
+
328
+ ## 🧪 Advanced Usage
329
+
330
+ ### Manual Rate Limiting (Custom Frameworks)
331
+
332
+ ```typescript
333
+ import { createRateLimiter } from '@periodic/titanium';
334
+
335
+ const limiter = createRateLimiter({
336
+ redis,
337
+ limit: 100,
338
+ window: 60,
339
+ keyPrefix: 'api'
340
+ });
341
+
342
+ // In your custom middleware/framework
343
+ async function customHandler(req, res) {
344
+ const identifier = getUserId(req) || getIp(req);
345
+
346
+ const result = await limiter.limit(identifier);
347
+
348
+ if (!result.allowed) {
349
+ return res.status(429).json({
350
+ error: 'Rate limit exceeded',
351
+ retryAfter: result.ttl
352
+ });
353
+ }
354
+
355
+ // Process request...
356
+ }
357
+ ```
358
+
359
+ ### Utility Methods
360
+
361
+ ```typescript
362
+ import { createRateLimiter } from '@periodic/titanium';
363
+
364
+ const limiter = createRateLimiter({ /* config */ });
365
+
366
+ // Check current status
367
+ const status = await limiter.getStatus('user-123');
368
+ console.log(status);
369
+ // { current: 45, ttl: 30 } or null
370
+
371
+ // Reset rate limit (useful for testing or admin tools)
372
+ await limiter.reset('user-123');
373
+ ```
374
+
375
+ ---
376
+
377
+ ## 🎨 Custom Logger Integration
378
+
379
+ ```typescript
380
+ import winston from 'winston';
381
+
382
+ const logger = winston.createLogger({
383
+ level: 'info',
384
+ format: winston.format.json(),
385
+ transports: [new winston.transports.Console()]
386
+ });
387
+
388
+ app.use(rateLimit({
389
+ redis,
390
+ limit: 100,
391
+ window: 60,
392
+ keyPrefix: 'api',
393
+ logger: {
394
+ info: (...args) => logger.info(args.join(' ')),
395
+ warn: (...args) => logger.warn(args.join(' ')),
396
+ error: (...args) => logger.error(args.join(' '))
397
+ }
398
+ }));
399
+ ```
400
+
401
+ ---
402
+
403
+ ## 🔍 Monitoring & Debugging
404
+
405
+ ### Log Levels
406
+
407
+ **INFO:** Rate limit checks and warnings (80% threshold)
408
+ ```
409
+ Rate limit check: identifier=ratelimit:api:192.168.1.1, count=45/100, allowed=true
410
+ Rate limit warning for identifier: ratelimit:api:192.168.1.1 - 18 remaining
411
+ ```
412
+
413
+ **WARN:** Rate limits exceeded, Redis failures
414
+ ```
415
+ Rate limit exceeded for identifier: user-123 (create-post)
416
+ Redis unavailable with fail-open strategy - allowing request
417
+ ```
418
+
419
+ **ERROR:** Unexpected errors
420
+ ```
421
+ Rate limiter error: Error: Connection refused
422
+ ```
423
+
424
+ ### Recommended Metrics to Track
425
+
426
+ 1. **Rate limit hits per endpoint** (counter)
427
+ 2. **Redis availability** (gauge)
428
+ 3. **95th percentile remaining requests** (histogram)
429
+ 4. **Fail-open events** (counter)
430
+
431
+ ---
432
+
433
+ ## 🛠️ Production Recommendations
434
+
435
+ ### Redis Configuration
436
+
437
+ ```typescript
438
+ import { createClient } from 'redis';
439
+
440
+ const redis = createClient({
441
+ url: process.env.REDIS_URL,
442
+ socket: {
443
+ reconnectStrategy: (retries) => {
444
+ if (retries > 10) return new Error('Max retries reached');
445
+ return Math.min(retries * 100, 3000); // Exponential backoff
446
+ }
447
+ }
448
+ });
449
+
450
+ redis.on('error', (err) => console.error('Redis error:', err));
451
+ redis.on('reconnecting', () => console.log('Redis reconnecting...'));
452
+
453
+ await redis.connect();
454
+ ```
455
+
456
+ ### Rate Limit Guidelines
457
+
458
+ | Endpoint Type | Recommended Limit | Window | Reasoning |
459
+ |---------------|-------------------|--------|-----------|
460
+ | **Authentication** | 5-10 | 300s (5 min) | Prevent brute force |
461
+ | **Expensive AI/ML** | 3-5 | 3600s (1 hour) | High compute cost |
462
+ | **Write Operations** | 10-50 | 60s | Prevent spam |
463
+ | **Read Operations** | 100-1000 | 60s | Allow browsing |
464
+ | **Global API** | 1000-5000 | 3600s | Safety net |
465
+
466
+ ### Environment Variables
467
+
468
+ ```bash
469
+ # .env
470
+ REDIS_URL=redis://localhost:6379
471
+
472
+ # Production
473
+ REDIS_URL=redis://username:password@redis-host:6379
474
+
475
+ # Redis Cluster
476
+ REDIS_URL=redis://redis-cluster:6379
477
+ ```
478
+
479
+ ---
480
+
481
+ ## ⚠️ Important Considerations
482
+
483
+ ### Redis Persistence
484
+
485
+ Rate limiting **does not require** Redis persistence (RDB/AOF). If Redis restarts, rate limits reset—which is acceptable for most applications.
486
+
487
+ If you need guaranteed limits across restarts, enable Redis persistence:
488
+ ```bash
489
+ redis-server --appendonly yes
490
+ ```
491
+
492
+ ### Horizontal Scaling
493
+
494
+ This package works seamlessly across multiple app instances because:
495
+ - All state is stored in Redis
496
+ - Redis operations are atomic (INCR, SET NX)
497
+ - No coordination between instances needed
498
+
499
+ ### IPv6 Handling
500
+
501
+ The package automatically normalizes IPv6-mapped IPv4 addresses:
502
+ ```
503
+ ::ffff:192.168.1.1 → 192.168.1.1
504
+ ```
505
+
506
+ ---
507
+
508
+ ## 🚫 Explicit Non-Goals
509
+
510
+ This package **intentionally does not** include:
511
+
512
+ ❌ Sliding window log algorithm (use fixed window with shorter intervals instead)
513
+ ❌ Token bucket or leaky bucket algorithms (may be added in v2.x)
514
+ ❌ Built-in Redis clustering logic (use Redis Cluster directly)
515
+ ❌ Distributed tracing or metrics export (integrate with your APM)
516
+ ❌ Framework auto-detection (explicit configuration only)
517
+ ❌ In-memory fallback (defeats the purpose of distributed rate limiting)
518
+
519
+ If you need these features, consider:
520
+ - **express-rate-limit** for in-memory limiting
521
+ - **rate-limiter-flexible** for advanced algorithms
522
+ - Building a custom solution on top of the core `RateLimiter` class
523
+
524
+ ---
525
+
526
+ ## 🧩 Architecture
527
+
528
+ ```
529
+ @periodic/titanium/
530
+ ├── src/
531
+ │ ├── core/
532
+ │ │ ├── limiter.ts # Framework-agnostic rate limiter
533
+ │ │ └── types.ts # TypeScript interfaces
534
+ │ ├── adapters/
535
+ │ │ └── express.ts # Express middleware adapter
536
+ │ ├── utils/
537
+ │ │ └── ip.ts # IP extraction utilities
538
+ │ └── index.ts # Public API exports
539
+ ```
540
+
541
+ **Design Philosophy:**
542
+ - **Core** is pure TypeScript, no framework dependencies
543
+ - **Adapters** connect core to specific frameworks (Express, Fastify, etc.)
544
+ - **Utils** provide reusable helper functions
545
+
546
+ This allows you to:
547
+ - Use the core `RateLimiter` directly in non-Express apps
548
+ - Build custom adapters for other frameworks
549
+ - Test components independently
550
+
551
+ ---
552
+
553
+ ## 📖 API Reference
554
+
555
+ ### `rateLimit(options)`
556
+
557
+ Creates Express middleware for rate limiting.
558
+
559
+ **Returns:** `(req, res, next) => Promise<void>`
560
+
561
+ ### `createRateLimiter(options)`
562
+
563
+ Creates standalone rate limiter instance.
564
+
565
+ **Returns:** `RateLimiter`
566
+
567
+ ### `RateLimiter` Methods
568
+
569
+ - `limit(identifier: string): Promise<RateLimitResult>`
570
+ - `reset(identifier: string): Promise<boolean>`
571
+ - `getStatus(identifier: string): Promise<{ current, ttl } | null>`
572
+
573
+ ---
574
+
575
+ ## 🤝 Contributing
576
+
577
+ Contributions are welcome! Please:
578
+
579
+ 1. Fork the repository
580
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
581
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
582
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
583
+ 5. Open a Pull Request
584
+
585
+ ---
586
+
587
+ ## 📄 License
588
+
589
+ MIT License - see [LICENSE](LICENSE) file for details.
590
+
591
+ ---
592
+
593
+ ## 🙏 Acknowledgments
594
+
595
+ - Inspired by [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
596
+ - Built with production lessons from scaling APIs to millions of requests
597
+
598
+ ---
599
+
600
+ ## 📞 Support
601
+
602
+ - 🐛 **Issues:** [GitHub Issues](https://github.com/thaku7469/periodic-titanium/issues)
603
+ - 💬 **Discussions:** [GitHub Discussions](https://github.com/thaku7469/periodic-titanium/discussions)
604
+ - 📧 **Email:** udaythakurwork@gmail.com
605
+
606
+ ---
607
+
608
+ **Built with ❤️ for production-grade Node.js applications**
@@ -0,0 +1,71 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { RateLimiter } from "../core/limiter";
3
+ import { ExpressRateLimitOptions } from "../core/types";
4
+ /**
5
+ * Express rate limiting middleware factory
6
+ *
7
+ * Features:
8
+ * - Framework-agnostic core with Express adapter
9
+ * - Configurable identifier extraction (user ID, API key, IP, etc.)
10
+ * - Fail-open or fail-closed strategies
11
+ * - Standard HTTP rate limit headers
12
+ * - Skip function for conditional rate limiting
13
+ *
14
+ * @param options - Express rate limit configuration
15
+ * @returns Express middleware function
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { createClient } from 'redis';
20
+ * import { rateLimit } from '@periodic/titanium';
21
+ *
22
+ * const redis = createClient();
23
+ * await redis.connect();
24
+ *
25
+ * // Basic usage with IP-based rate limiting
26
+ * app.use(rateLimit({
27
+ * redis,
28
+ * limit: 100,
29
+ * window: 60,
30
+ * keyPrefix: 'api'
31
+ * }));
32
+ *
33
+ * // User-based rate limiting with JWT
34
+ * app.post('/api/resource',
35
+ * authMiddleware,
36
+ * rateLimit({
37
+ * redis,
38
+ * limit: 10,
39
+ * window: 60,
40
+ * keyPrefix: 'create-resource',
41
+ * identifier: (req) => req.user?.id?.toString() || null
42
+ * }),
43
+ * handler
44
+ * );
45
+ * ```
46
+ */
47
+ export declare function rateLimit(options: ExpressRateLimitOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
48
+ /**
49
+ * Create a rate limiter instance for manual control
50
+ * Useful for custom implementations or non-Express frameworks
51
+ *
52
+ * @param options - Rate limiter configuration
53
+ * @returns RateLimiter instance
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const limiter = createRateLimiter({
58
+ * redis,
59
+ * limit: 100,
60
+ * window: 60,
61
+ * keyPrefix: 'api'
62
+ * });
63
+ *
64
+ * const result = await limiter.limit('user-123');
65
+ * if (!result.allowed) {
66
+ * // Handle rate limit exceeded
67
+ * }
68
+ * ```
69
+ */
70
+ export declare function createRateLimiter(options: Omit<ExpressRateLimitOptions, "identifier" | "skip" | "message" | "failStrategy" | "standardHeaders">): RateLimiter;
71
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAyB,MAAM,eAAe,CAAC;AAG/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,uBAAuB,IAoDtD,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CA2FjB;AAeD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CACX,uBAAuB,EACvB,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,iBAAiB,CACvE,GACA,WAAW,CAEb"}