UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

306 lines (185 loc) β€’ 6.94 kB
# 🧠 `TinyRateLimiter` – A Simple Per-User Rate Limiter The `TinyRateLimiter` is a flexible and lightweight JavaScript class designed to help you limit actions (like requests or commands) per user. You can define rules like **maximum hits**, **time windows**, and benefit from automatic memory cleanup. ✨ --- ## πŸ“¦ Constructor ```js new TinyRateLimiter(options) ``` ### πŸ”§ Options | Option | Type | Default | Description | | ----------------- | -------- | ----------- | ---------------------------------------------- | | `maxHits` | `number` | `undefined` | Max number of hits per user before blocking 🚧 | | `interval` | `number` | `undefined` | Time window in milliseconds ⏱️ | | `cleanupInterval` | `number` | `undefined` | Interval to auto-clean inactive users 🧹 | | `maxIdle` | `number` | `300000` | Max idle time per user before cleanup πŸ’€ | | `maxMemory` | `number` | `100000` | Max memory size limit before auto cleanup 🚧 | > ⚠️ At least one of `maxHits` or `interval` must be defined. --- ## πŸ“‹ Methods ### πŸš€ `hit(userId: string): void` Registers a hit for the given `userId`. ```js rateLimiter.hit("user123"); ``` Tracks timestamps internally and automatically removes old entries based on `interval` and `maxHits`. --- ### ❌ `isRateLimited(userId: string): boolean` Checks if the given `userId` is currently rate limited. ```js if (rateLimiter.isRateLimited("user123")) { console.log("Too many actions! Please slow down."); } ``` --- ### 🧾 `isGroupId(id: string): boolean` Checks whether a given `id` is marked as a **groupId** (i.e., it represents a group and not a single user). ```js if (rateLimiter.isGroupId('group-xyz')) { console.log('This is a group ID!'); } ``` --- ### πŸ› οΈ `setData(userId: string, timestamps: number[]): void` Sets the hit timestamps for a specific user. Useful for restoring or mocking state. ```js rateLimiter.setData("user123", [Date.now() - 1000, Date.now()]); ``` --- ### πŸ“₯ `getData(userId: string): number[]` Returns all hit timestamps for a given user. ```js const hits = rateLimiter.getData("user123"); console.log(hits); // [timestamp1, timestamp2, ...] ``` --- ### 🧠 `getMaxHits(): number` Returns the configured `maxHits` value, or throws if invalid. --- ### ⏳ `getInterval(): number` Returns the configured `interval` value, or throws if invalid. --- ### πŸ‘€ `getLastHit(userId: string): number|null` Returns the timestamp of the user's last hit, or `null` if none. --- ### βŒ› `getTimeSinceLastHit(userId: string): number|null` Returns how much time (in ms) has passed since the user's last hit. --- ### πŸ“Š `getAverageHitSpacing(userId: string): number|null` Returns the average time (in ms) between a user's hits, or `null` if not enough data. --- ### πŸ”— `getGroupId(userId: string): string` Returns the group ID that the user is assigned to (default is same as userId). --- ### πŸ“¦ `assignToGroup(userId: string, groupId: string): void` Manually assigns a user to a specific group for shared rate limits. --- ### πŸ’‘ `getGroupTTL(groupId: string): number|undefined` Returns the custom TTL (ms) for a specific group, if defined. --- ### πŸ•°οΈ `setGroupTTL(groupId: string, ttl: number): void` Sets the maximum idle time before a group is auto-cleaned. --- ### πŸ“Š `getMetrics(groupId: string): { totalHits, lastHit, timeSinceLastHit, averageHitSpacing }` Returns a detailed summary of activity for a group: ```js { totalHits: number, lastHit: number|null, timeSinceLastHit: number|null, averageHitSpacing: number|null } ``` --- ### 🧼 `destroy(): void` Stops internal timers and clears all stored data. Ideal for cleanup in tests or long-running apps. --- ### 🧠 `setOnMemoryExceeded(callback: () => void): void` Sets a callback to be called when memory usage (based on internal data size) exceeds a threshold. ```js rateLimiter.setOnMemoryExceeded(() => { console.warn("Rate limiter memory usage is high!"); }); ``` --- ### 🧽 `clearOnMemoryExceeded(): void` Clears the memory-exceeded callback. --- ### πŸ•“ `setOnGroupExpired(callback: (groupId: string) => void): void` Sets a callback that will be called whenever a group expires due to inactivity (based on TTL or `maxIdle`). ```js rateLimiter.setOnGroupExpired((groupId) => { console.log(`Group "${groupId}" has expired.`); }); ``` --- ### πŸ”• `clearOnGroupExpired(): void` Removes the group expiration callback. --- ### ❌ `deleteGroupTTL(groupId: string): void` Removes any custom TTL (idle timeout) for a specific group. ```js rateLimiter.deleteGroupTTL("admins"); ``` --- ### ♻️ `resetGroup(groupId: string): void` Removes all hit history and data for a group (but keeps user–group associations unless manually removed). --- ### πŸ‘€ `resetUserGroup(userId: string): void` Fully removes a user's data about group assignment. --- ### πŸ” `hasData(userId: string): boolean` Returns `true` if the user has any stored data (hits or group association). --- ### πŸ‘₯ `getUsersInGroup(groupId: string): string[]` Returns a list of users assigned to the given group. --- ### πŸ’€ `getMaxIdle(): number` Returns the current global `maxIdle` value (in ms) used for cleanup. --- ### πŸ’€ `setMaxIdle(ms: number): void` Sets the global `maxIdle` value (in ms), which applies to all groups without a custom TTL. --- ### πŸ“ˆ `getTotalHits(groupId: string): number` Returns the total number of hits recorded for a given group. ```js const total = rateLimiter.getTotalHits("group42"); console.log(`Group total hits: ${total}`); ``` --- ## 🧽 Automatic Cleanup Inactive groups are automatically removed every `cleanupInterval` milliseconds if they haven't had any hits for longer than their TTL (or `maxIdle` if not set). This helps reduce memory usage and keeps your limiter lean and clean! πŸͺΆ --- ## πŸ’₯ Errors The constructor will throw if: * Neither `maxHits` nor `interval` are provided. * Any of the values are not positive integers. --- ## πŸ§ͺ Example Usage ```js const limiter = new TinyRateLimiter({ maxHits: 5, interval: 10000, cleanupInterval: 60000, maxIdle: 120000, maxMemory: 100000, }); limiter.hit("user42"); if (limiter.isRateLimited("user42")) { console.log("Rate limited! πŸ›‘"); } else { console.log("Action allowed βœ…"); } ``` --- ## 🌈 Summary | Feature | Support | | -------------------------- | ------- | | Per-user tracking | βœ… | | Max hits | βœ… | | Time window interval | βœ… | | Automatic cleanup | βœ… | | Manual data control | βœ… | | Lightweight + fast | βœ… | | Group-based rate limiting | βœ… | | Activity metrics per group | βœ… |