Socket
Socket
Sign inDemoInstall

@nestjs/throttler

Package Overview
Dependencies
Maintainers
2
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nestjs/throttler - npm Package Compare versions

Comparing version 5.2.0 to 6.0.0

3

dist/hash.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.md5 = void 0;
exports.md5 = md5;
const crypto = require("node:crypto");

@@ -8,3 +8,2 @@ function md5(text) {

}
exports.md5 = md5;
//# sourceMappingURL=hash.js.map

@@ -9,2 +9,3 @@ import { ExecutionContext, ModuleMetadata, Type } from '@nestjs/common/interfaces';

ttl: Resolvable<number>;
blockDuration?: Resolvable<number>;
ignoreUserAgents?: RegExp[];

@@ -11,0 +12,0 @@ skipIf?: (context: ExecutionContext) => boolean;

export interface ThrottlerStorageOptions {
totalHits: number;
totalHits: Map<string, number>;
expiresAt: number;
isBlocked: boolean;
blockExpiresAt: number;
}
export declare const ThrottlerStorageOptions: unique symbol;
export interface ThrottlerStorageRecord {
totalHits: number;
timeToExpire: number;
isBlocked: boolean;
timeToBlockExpire: number;
}
export declare const ThrottlerStorageRecord: unique symbol;
import { ThrottlerStorageRecord } from './throttler-storage-record.interface';
export interface ThrottlerStorage {
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
increment(key: string, ttl: number, limit: number, blockDuration: number, throttlerName: string): Promise<ThrottlerStorageRecord>;
}
export declare const ThrottlerStorage: unique symbol;
export declare const THROTTLER_LIMIT = "THROTTLER:LIMIT";
export declare const THROTTLER_TTL = "THROTTLER:TTL";
export declare const THROTTLER_TRACKER = "THROTTLER:TRACKER";
export declare const THROTTLER_BLOCK_DURATION = "THROTTLER:BLOCK_DURATION";
export declare const THROTTLER_KEY_GENERATOR = "THROTTLER:KEY_GENERATOR";
export declare const THROTTLER_OPTIONS = "THROTTLER:MODULE_OPTIONS";
export declare const THROTTLER_SKIP = "THROTTLER:SKIP";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.THROTTLER_SKIP = exports.THROTTLER_OPTIONS = exports.THROTTLER_KEY_GENERATOR = exports.THROTTLER_TRACKER = exports.THROTTLER_TTL = exports.THROTTLER_LIMIT = void 0;
exports.THROTTLER_SKIP = exports.THROTTLER_OPTIONS = exports.THROTTLER_KEY_GENERATOR = exports.THROTTLER_BLOCK_DURATION = exports.THROTTLER_TRACKER = exports.THROTTLER_TTL = exports.THROTTLER_LIMIT = void 0;
exports.THROTTLER_LIMIT = 'THROTTLER:LIMIT';
exports.THROTTLER_TTL = 'THROTTLER:TTL';
exports.THROTTLER_TRACKER = 'THROTTLER:TRACKER';
exports.THROTTLER_BLOCK_DURATION = 'THROTTLER:BLOCK_DURATION';
exports.THROTTLER_KEY_GENERATOR = 'THROTTLER:KEY_GENERATOR';

@@ -8,0 +9,0 @@ exports.THROTTLER_OPTIONS = 'THROTTLER:MODULE_OPTIONS';

@@ -5,2 +5,3 @@ import { Resolvable, ThrottlerGenerateKeyFunction, ThrottlerGetTrackerFunction } from './throttler-module-options.interface';

ttl?: Resolvable<number>;
blockDuration?: Resolvable<number>;
getTracker?: ThrottlerGetTrackerFunction;

@@ -7,0 +8,0 @@ generateKey?: ThrottlerGenerateKeyFunction;

@@ -11,2 +11,3 @@ "use strict";

Reflect.defineMetadata(throttler_constants_1.THROTTLER_LIMIT + name, options[name].limit, target);
Reflect.defineMetadata(throttler_constants_1.THROTTLER_BLOCK_DURATION + name, options[name].blockDuration, target);
Reflect.defineMetadata(throttler_constants_1.THROTTLER_TRACKER + name, options[name].getTracker, target);

@@ -13,0 +14,0 @@ Reflect.defineMetadata(throttler_constants_1.THROTTLER_KEY_GENERATOR + name, options[name].generateKey, target);

import { CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ThrottlerGenerateKeyFunction, ThrottlerGetTrackerFunction, ThrottlerModuleOptions, ThrottlerOptions } from './throttler-module-options.interface';
import { ThrottlerModuleOptions, ThrottlerOptions } from './throttler-module-options.interface';
import { ThrottlerStorage } from './throttler-storage.interface';
import { ThrottlerLimitDetail } from './throttler.guard.interface';
import { ThrottlerLimitDetail, ThrottlerRequest } from './throttler.guard.interface';
export declare class ThrottlerGuard implements CanActivate {

@@ -18,3 +18,3 @@ protected readonly options: ThrottlerModuleOptions;

protected shouldSkip(_context: ExecutionContext): Promise<boolean>;
protected handleRequest(context: ExecutionContext, limit: number, ttl: number, throttler: ThrottlerOptions, getTracker: ThrottlerGetTrackerFunction, generateKey: ThrottlerGenerateKeyFunction): Promise<boolean>;
protected handleRequest(requestProps: ThrottlerRequest): Promise<boolean>;
protected getTracker(req: Record<string, any>): Promise<string>;

@@ -21,0 +21,0 @@ protected getRequestResponse(context: ExecutionContext): {

@@ -0,2 +1,4 @@

import { ExecutionContext } from '@nestjs/common';
import { ThrottlerStorageRecord } from './throttler-storage-record.interface';
import { ThrottlerGenerateKeyFunction, ThrottlerGetTrackerFunction, ThrottlerOptions } from './throttler-module-options.interface';
export interface ThrottlerLimitDetail extends ThrottlerStorageRecord {

@@ -8,1 +10,10 @@ ttl: number;

}
export interface ThrottlerRequest {
context: ExecutionContext;
limit: number;
ttl: number;
throttler: ThrottlerOptions;
blockDuration: number;
getTracker: ThrottlerGetTrackerFunction;
generateKey: ThrottlerGenerateKeyFunction;
}

@@ -78,2 +78,3 @@ "use strict";

const routeOrClassTtl = this.reflector.getAllAndOverride(throttler_constants_1.THROTTLER_TTL + namedThrottler.name, [handler, classRef]);
const routeOrClassBlockDuration = this.reflector.getAllAndOverride(throttler_constants_1.THROTTLER_BLOCK_DURATION + namedThrottler.name, [handler, classRef]);
const routeOrClassGetTracker = this.reflector.getAllAndOverride(throttler_constants_1.THROTTLER_TRACKER + namedThrottler.name, [handler, classRef]);

@@ -83,5 +84,14 @@ const routeOrClassGetKeyGenerator = this.reflector.getAllAndOverride(throttler_constants_1.THROTTLER_KEY_GENERATOR + namedThrottler.name, [handler, classRef]);

const ttl = await this.resolveValue(context, routeOrClassTtl || namedThrottler.ttl);
const blockDuration = await this.resolveValue(context, routeOrClassBlockDuration || namedThrottler.blockDuration || ttl);
const getTracker = routeOrClassGetTracker || namedThrottler.getTracker || this.commonOptions.getTracker;
const generateKey = routeOrClassGetKeyGenerator || namedThrottler.generateKey || this.commonOptions.generateKey;
continues.push(await this.handleRequest(context, limit, ttl, namedThrottler, getTracker, generateKey));
continues.push(await this.handleRequest({
context,
limit,
ttl,
throttler: namedThrottler,
blockDuration,
getTracker,
generateKey,
}));
}

@@ -93,4 +103,5 @@ return continues.every((cont) => cont);

}
async handleRequest(context, limit, ttl, throttler, getTracker, generateKey) {
async handleRequest(requestProps) {
var _a;
const { context, limit, ttl, throttler, blockDuration, getTracker, generateKey } = requestProps;
const { req, res } = this.getRequestResponse(context);

@@ -107,6 +118,6 @@ const ignoreUserAgents = (_a = throttler.ignoreUserAgents) !== null && _a !== void 0 ? _a : this.commonOptions.ignoreUserAgents;

const key = generateKey(context, tracker, throttler.name);
const { totalHits, timeToExpire } = await this.storageService.increment(key, ttl);
const { totalHits, timeToExpire, isBlocked, timeToBlockExpire } = await this.storageService.increment(key, ttl, limit, blockDuration, throttler.name);
const getThrottlerSuffix = (name) => (name === 'default' ? '' : `-${name}`);
if (totalHits > limit) {
res.header(`Retry-After${getThrottlerSuffix(throttler.name)}`, timeToExpire);
if (isBlocked) {
res.header(`Retry-After${getThrottlerSuffix(throttler.name)}`, timeToBlockExpire);
await this.throwThrottlingException(context, {

@@ -119,2 +130,4 @@ limit,

timeToExpire,
isBlocked,
timeToBlockExpire,
});

@@ -121,0 +134,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStorageToken = exports.getOptionsToken = exports.ThrottlerStorageProvider = exports.createThrottlerProviders = void 0;
exports.getStorageToken = exports.getOptionsToken = exports.ThrottlerStorageProvider = void 0;
exports.createThrottlerProviders = createThrottlerProviders;
const throttler_storage_interface_1 = require("./throttler-storage.interface");

@@ -15,3 +16,2 @@ const throttler_constants_1 = require("./throttler.constants");

}
exports.createThrottlerProviders = createThrottlerProviders;
exports.ThrottlerStorageProvider = {

@@ -18,0 +18,0 @@ provide: throttler_storage_interface_1.ThrottlerStorage,

@@ -8,7 +8,11 @@ import { OnApplicationShutdown } from '@nestjs/common';

private timeoutIds;
get storage(): Record<string, ThrottlerStorageOptions>;
get storage(): Map<string, ThrottlerStorageOptions>;
private getExpirationTime;
private getBlockExpirationTime;
private setExpirationTime;
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
private clearExpirationTimes;
private resetBlockdRequest;
private fireHitCount;
increment(key: string, ttl: number, limit: number, blockDuration: number, throttlerName: string): Promise<ThrottlerStorageRecord>;
onApplicationShutdown(): void;
}

@@ -13,4 +13,4 @@ "use strict";

constructor() {
this._storage = {};
this.timeoutIds = [];
this._storage = new Map();
this.timeoutIds = new Map();
}

@@ -21,31 +21,71 @@ get storage() {

getExpirationTime(key) {
return Math.floor((this.storage[key].expiresAt - Date.now()) / 1000);
return Math.floor((this.storage.get(key).expiresAt - Date.now()) / 1000);
}
setExpirationTime(key, ttlMilliseconds) {
getBlockExpirationTime(key) {
return Math.floor((this.storage.get(key).blockExpiresAt - Date.now()) / 1000);
}
setExpirationTime(key, ttlMilliseconds, throttlerName) {
const timeoutId = setTimeout(() => {
this.storage[key].totalHits--;
const { totalHits } = this.storage.get(key);
totalHits.set(throttlerName, totalHits.get(throttlerName) - 1);
clearTimeout(timeoutId);
this.timeoutIds = this.timeoutIds.filter((id) => id != timeoutId);
this.timeoutIds.set(throttlerName, this.timeoutIds.get(throttlerName).filter((id) => id !== timeoutId));
}, ttlMilliseconds);
this.timeoutIds.push(timeoutId);
this.timeoutIds.get(throttlerName).push(timeoutId);
}
async increment(key, ttl) {
clearExpirationTimes(throttlerName) {
this.timeoutIds.get(throttlerName).forEach(clearTimeout);
this.timeoutIds.set(throttlerName, []);
}
resetBlockdRequest(key, throttlerName) {
this.storage.get(key).isBlocked = false;
this.storage.get(key).totalHits.set(throttlerName, 0);
this.clearExpirationTimes(throttlerName);
}
fireHitCount(key, throttlerName, ttl) {
const { totalHits } = this.storage.get(key);
totalHits.set(throttlerName, totalHits.get(throttlerName) + 1);
this.setExpirationTime(key, ttl, throttlerName);
}
async increment(key, ttl, limit, blockDuration, throttlerName) {
const ttlMilliseconds = ttl;
if (!this.storage[key]) {
this.storage[key] = { totalHits: 0, expiresAt: Date.now() + ttlMilliseconds };
const blockDurationMilliseconds = blockDuration;
if (!this.timeoutIds.has(throttlerName)) {
this.timeoutIds.set(throttlerName, []);
}
if (!this.storage.has(key)) {
this.storage.set(key, {
totalHits: new Map([[throttlerName, 0]]),
expiresAt: Date.now() + ttlMilliseconds,
blockExpiresAt: 0,
isBlocked: false,
});
}
let timeToExpire = this.getExpirationTime(key);
if (timeToExpire <= 0) {
this.storage[key].expiresAt = Date.now() + ttlMilliseconds;
this.storage.get(key).expiresAt = Date.now() + ttlMilliseconds;
timeToExpire = this.getExpirationTime(key);
}
this.storage[key].totalHits++;
this.setExpirationTime(key, ttlMilliseconds);
if (!this.storage.get(key).isBlocked) {
this.fireHitCount(key, throttlerName, ttlMilliseconds);
}
if (this.storage.get(key).totalHits.get(throttlerName) > limit &&
!this.storage.get(key).isBlocked) {
this.storage.get(key).isBlocked = true;
this.storage.get(key).blockExpiresAt = Date.now() + blockDurationMilliseconds;
}
const timeToBlockExpire = this.getBlockExpirationTime(key);
if (timeToBlockExpire <= 0 && this.storage.get(key).isBlocked) {
this.resetBlockdRequest(key, throttlerName);
this.fireHitCount(key, throttlerName, ttlMilliseconds);
}
return {
totalHits: this.storage[key].totalHits,
totalHits: this.storage.get(key).totalHits.get(throttlerName),
timeToExpire,
isBlocked: this.storage.get(key).isBlocked,
timeToBlockExpire: timeToBlockExpire,
};
}
onApplicationShutdown() {
this.timeoutIds.forEach(clearTimeout);
this.timeoutIds.forEach((timeouts) => timeouts.forEach(clearTimeout));
}

@@ -52,0 +92,0 @@ };

{
"name": "@nestjs/throttler",
"version": "5.2.0",
"version": "6.0.0",
"description": "A Rate-Limiting module for NestJS to work on Express, Fastify, Websockets, Socket.IO, and GraphQL, all rolled up into a simple package.",

@@ -29,24 +29,24 @@ "author": "Jay McDoniel <me@jaymcdoniel.dev>",

"@apollo/server": "4.10.4",
"@changesets/cli": "2.27.5",
"@changesets/cli": "2.27.7",
"@commitlint/cli": "19.3.0",
"@commitlint/config-angular": "19.3.0",
"@nestjs/cli": "10.3.2",
"@nestjs/common": "10.3.9",
"@nestjs/core": "10.3.9",
"@nestjs/graphql": "12.1.1",
"@nestjs/platform-express": "10.3.9",
"@nestjs/platform-fastify": "10.3.9",
"@nestjs/platform-socket.io": "10.3.9",
"@nestjs/platform-ws": "10.3.9",
"@nestjs/schematics": "10.1.1",
"@nestjs/testing": "10.3.9",
"@nestjs/websockets": "10.3.9",
"@nestjs/cli": "10.4.2",
"@nestjs/common": "10.3.10",
"@nestjs/core": "10.3.10",
"@nestjs/graphql": "12.2.0",
"@nestjs/platform-express": "10.3.10",
"@nestjs/platform-fastify": "10.3.10",
"@nestjs/platform-socket.io": "10.3.10",
"@nestjs/platform-ws": "10.3.10",
"@nestjs/schematics": "10.1.2",
"@nestjs/testing": "10.3.10",
"@nestjs/websockets": "10.3.10",
"@semantic-release/git": "10.0.1",
"@types/express": "4.17.21",
"@types/express-serve-static-core": "4.19.3",
"@types/express-serve-static-core": "4.19.5",
"@types/jest": "29.5.12",
"@types/node": "20.14.2",
"@types/node": "20.14.10",
"@types/supertest": "6.0.2",
"@typescript-eslint/eslint-plugin": "7.13.0",
"@typescript-eslint/parser": "7.13.0",
"@typescript-eslint/eslint-plugin": "7.16.0",
"@typescript-eslint/parser": "7.16.0",
"apollo-server-fastify": "3.13.0",

@@ -58,3 +58,3 @@ "conventional-changelog-cli": "5.0.0",

"eslint-plugin-import": "2.29.1",
"graphql": "16.8.2",
"graphql": "16.9.0",
"graphql-tools": "9.0.1",

@@ -64,3 +64,3 @@ "husky": "9.0.11",

"lint-staged": "15.2.7",
"nodemon": "3.1.3",
"nodemon": "3.1.4",
"pactum": "^3.4.1",

@@ -70,12 +70,12 @@ "pinst": "3.0.0",

"reflect-metadata": "0.2.2",
"rimraf": "5.0.7",
"rimraf": "6.0.1",
"rxjs": "7.8.1",
"socket.io": "4.7.5",
"supertest": "7.0.0",
"ts-jest": "29.1.4",
"ts-jest": "29.2.2",
"ts-loader": "9.5.1",
"ts-node": "10.9.2",
"tsconfig-paths": "4.2.0",
"typescript": "5.4.5",
"ws": "8.17.0"
"typescript": "5.5.3",
"ws": "8.18.0"
},

@@ -82,0 +82,0 @@ "peerDependencies": {

<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
[travis-url]: https://travis-ci.org/nestjs/nest
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
[linux-url]: https://travis-ci.org/nestjs/nest

@@ -276,2 +272,6 @@ <p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>

<tr>
<td><code>blockDuration</code></td>
<td>the number of milliseconds that request will be blocked for that time</td>
</tr>
<tr>
<td><code>ignoreUserAgents</code></td>

@@ -278,0 +278,0 @@ <td>an array of regular expressions of user-agents to ignore when it comes to throttling requests</td>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc