nestjs-shield 1.0.1 → 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 CHANGED
@@ -37,7 +37,7 @@ Or from GitHub Packages (published as `@yassinos-coder/nestjs-shield`) — first
37
37
  GITHUB_TOKEN=ghp_xxx npm install @yassinos-coder/nestjs-shield
38
38
  ```
39
39
 
40
- Node ≥ 18, NestJS 9 / 10 / 11.
40
+ Node ≥ 18, NestJS 9 / 10 / 11 / 12. NestJS 12 applications require Node ≥ 20.
41
41
 
42
42
  ## Quick start
43
43
 
@@ -111,6 +111,19 @@ local ttl = redis.call('PTTL', key)
111
111
  if ttl < 0 then ttl = 0 end
112
112
  return { count, ttl }
113
113
  `;
114
+ const BURST_INCREMENT_LUA = `
115
+ local count = redis.call('INCR', KEYS[1])
116
+ redis.call('PEXPIRE', KEYS[1], ARGV[1])
117
+ return count
118
+ `;
119
+ const BURST_DECREMENT_LUA = `
120
+ local count = tonumber(redis.call('GET', KEYS[1]) or '0')
121
+ if count <= 1 then
122
+ redis.call('DEL', KEYS[1])
123
+ return 0
124
+ end
125
+ return redis.call('DECR', KEYS[1])
126
+ `;
114
127
  const SLIDING_COUNTER_LUA = `
115
128
  local cur = KEYS[1]
116
129
  local prev = KEYS[2]
@@ -166,11 +179,9 @@ class RedisStorage {
166
179
  }
167
180
  async fixedWindow(key, ttlMs, limit) {
168
181
  const k = this.k(key);
169
- const count = await this.client.incr(k);
170
- if (count === 1)
171
- await this.client.pexpire(k, ttlMs);
172
- const ttl = await this.client.pttl(k);
173
- return { count, allowed: count <= limit, resetMs: Math.max(0, ttl), limit };
182
+ const res = (await this.runScript('shieldIncrement', INCREMENT_LUA, [k], [1, ttlMs]));
183
+ const count = Number(res[0]);
184
+ return { count, allowed: count <= limit, resetMs: Math.max(0, Number(res[1])), limit };
174
185
  }
175
186
  async slidingWindowCounter(key, ttlMs, limit) {
176
187
  const now = Date.now();
@@ -225,16 +236,13 @@ class RedisStorage {
225
236
  }
226
237
  async incrementConcurrent(key) {
227
238
  const k = this.k(`burst:${key}`);
228
- const next = await this.client.incr(k);
229
- await this.client.pexpire(k, 60_000);
230
- return next;
239
+ const next = await this.runScript('shieldBurstIncrement', BURST_INCREMENT_LUA, [k], [60_000]);
240
+ return Number(next);
231
241
  }
232
242
  async decrementConcurrent(key) {
233
243
  const k = this.k(`burst:${key}`);
234
- const next = await this.client.decr(k);
235
- if (next <= 0)
236
- await this.client.del(k);
237
- return Math.max(0, next);
244
+ const next = await this.runScript('shieldBurstDecrement', BURST_DECREMENT_LUA, [k], []);
245
+ return Math.max(0, Number(next));
238
246
  }
239
247
  k(key) {
240
248
  return this.prefix ? `${this.prefix}:${key}` : key;
@@ -255,6 +263,8 @@ class RedisStorage {
255
263
  this.client.defineCommand('shieldSlidingLog', { numberOfKeys: 1, lua: SLIDING_LOG_LUA });
256
264
  this.client.defineCommand('shieldSlidingCounter', { numberOfKeys: 2, lua: SLIDING_COUNTER_LUA });
257
265
  this.client.defineCommand('shieldIncrement', { numberOfKeys: 1, lua: INCREMENT_LUA });
266
+ this.client.defineCommand('shieldBurstIncrement', { numberOfKeys: 1, lua: BURST_INCREMENT_LUA });
267
+ this.client.defineCommand('shieldBurstDecrement', { numberOfKeys: 1, lua: BURST_DECREMENT_LUA });
258
268
  this.commandsDefined = true;
259
269
  }
260
270
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-shield",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Layered API protection for NestJS: rate limiting, IP allow/block lists, auto-ban, slow-down, UA filtering, burst caps, payload limits. Pluggable storage (memory/Redis).",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -51,8 +51,8 @@
51
51
  "node": ">=18"
52
52
  },
53
53
  "peerDependencies": {
54
- "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0",
55
- "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0",
54
+ "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0",
55
+ "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0",
56
56
  "reflect-metadata": "^0.1.13 || ^0.2.0",
57
57
  "rxjs": "^7.0.0"
58
58
  },
@@ -65,9 +65,9 @@
65
65
  "ipaddr.js": "^2.2.0"
66
66
  },
67
67
  "devDependencies": {
68
- "@nestjs/common": "^10.4.0",
69
- "@nestjs/core": "^10.4.0",
70
- "@types/node": "^20.12.0",
68
+ "@nestjs/common": "^12.0.1",
69
+ "@nestjs/core": "^12.0.1",
70
+ "@types/node": "^22.0.0",
71
71
  "ioredis": "^5.4.1",
72
72
  "reflect-metadata": "^0.2.2",
73
73
  "rimraf": "^5.0.7",