bucketflow 1.0.1 → 1.0.2
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 +78 -22
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -0
- package/dist/index.mjs +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,47 +8,94 @@ Lightweight in-memory rate limiting utilities for Node.js and TypeScript.
|
|
|
8
8
|
npm install bucketflow
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Public exports
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
`src/index.ts` currently exports these three classes:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
export { FixedWindow } from "./fixed-window";
|
|
17
|
+
export { TokenBucket } from "./token-bucket";
|
|
18
|
+
export { SlidingWindow } from "./sliding-window";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each limiter stores its counters in memory and exposes a `checkLimit(key)`
|
|
22
|
+
method. The `key` identifies the client, user, IP address, API key, or any
|
|
23
|
+
other group that should have its own limit.
|
|
24
|
+
|
|
25
|
+
## Using the limiters
|
|
26
|
+
|
|
27
|
+
### `TokenBucket`
|
|
14
28
|
|
|
15
29
|
```ts
|
|
16
30
|
import { TokenBucket } from "bucketflow";
|
|
17
31
|
|
|
18
|
-
const limiter = new TokenBucket(10,
|
|
32
|
+
const limiter = new TokenBucket(10, 1_000);
|
|
19
33
|
const result = limiter.checkLimit("user-123");
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
console.log(result);
|
|
36
|
+
// { allowed: true, message: "Too many requests", tokens: 9 }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Constructor:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
new TokenBucket(capacity, refillRate, message?)
|
|
24
43
|
```
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
- `capacity`: maximum number of tokens.
|
|
46
|
+
- `refillRate`: milliseconds between token refills.
|
|
47
|
+
- `message`: optional message returned when a request is rejected.
|
|
48
|
+
|
|
49
|
+
`checkLimit(key)` returns `{ allowed, message, tokens }`.
|
|
28
50
|
|
|
29
|
-
###
|
|
51
|
+
### `FixedWindow`
|
|
30
52
|
|
|
31
53
|
```ts
|
|
32
54
|
import { FixedWindow } from "bucketflow";
|
|
33
55
|
|
|
34
|
-
const limiter = new FixedWindow(100, 60_000);
|
|
56
|
+
const limiter = new FixedWindow(100, 60_000, "Too many requests");
|
|
35
57
|
const result = limiter.checkLimit("user-123");
|
|
58
|
+
|
|
59
|
+
console.log(result);
|
|
60
|
+
// { allowed: true, reqRemaining: 99, message: "Too many requests" }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Constructor:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
new FixedWindow(maxNum, window, message?)
|
|
36
67
|
```
|
|
37
68
|
|
|
38
|
-
|
|
39
|
-
|
|
69
|
+
- `maxNum`: maximum number of requests in the window.
|
|
70
|
+
- `window`: window duration in milliseconds.
|
|
71
|
+
- `message`: optional message returned when a request is rejected.
|
|
40
72
|
|
|
41
|
-
|
|
73
|
+
`checkLimit(key)` returns `{ allowed, reqRemaining, message }`.
|
|
74
|
+
|
|
75
|
+
### `SlidingWindow`
|
|
42
76
|
|
|
43
77
|
```ts
|
|
44
78
|
import { SlidingWindow } from "bucketflow";
|
|
45
79
|
|
|
46
|
-
const limiter = new SlidingWindow(60_000, 1_000, 100);
|
|
80
|
+
const limiter = new SlidingWindow(60_000, 1_000, 100, "Too many requests");
|
|
47
81
|
const result = limiter.checkLimit("user-123");
|
|
82
|
+
|
|
83
|
+
console.log(result);
|
|
84
|
+
// { allowed: true, counter: 1, message: "Too many requests" }
|
|
48
85
|
```
|
|
49
86
|
|
|
50
|
-
|
|
51
|
-
|
|
87
|
+
Constructor:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
new SlidingWindow(window, refreshRate, maxRequests, message?)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- `window`: window duration in milliseconds.
|
|
94
|
+
- `refreshRate`: counter refresh interval in milliseconds.
|
|
95
|
+
- `maxRequests`: maximum requests tracked by the limiter.
|
|
96
|
+
- `message`: optional message returned when a request is rejected.
|
|
97
|
+
|
|
98
|
+
`checkLimit(key)` returns `{ allowed, counter, message }`.
|
|
52
99
|
|
|
53
100
|
## Return values
|
|
54
101
|
|
|
@@ -86,6 +133,15 @@ app.use((req, res, next) => {
|
|
|
86
133
|
});
|
|
87
134
|
```
|
|
88
135
|
|
|
136
|
+
You can use `FixedWindow` or `SlidingWindow` in the same middleware by
|
|
137
|
+
replacing the limiter construction:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const limiter = new FixedWindow(100, 60_000);
|
|
141
|
+
// or
|
|
142
|
+
const limiter = new SlidingWindow(60_000, 1_000, 100);
|
|
143
|
+
```
|
|
144
|
+
|
|
89
145
|
### Fastify
|
|
90
146
|
|
|
91
147
|
Fastify can use a limiter in a `preHandler` hook:
|
|
@@ -140,13 +196,13 @@ export class RateLimitMiddleware implements NestMiddleware {
|
|
|
140
196
|
Register the middleware in a module with `MiddlewareConsumer`, or adapt the
|
|
141
197
|
same pattern into a NestJS guard when route-level control is preferred.
|
|
142
198
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
The package currently exports these classes:
|
|
199
|
+
The same middleware pattern works with `FixedWindow` and `SlidingWindow`:
|
|
146
200
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
201
|
+
```ts
|
|
202
|
+
private readonly limiter = new FixedWindow(100, 60_000);
|
|
203
|
+
// or
|
|
204
|
+
private readonly limiter = new SlidingWindow(60_000, 1_000, 100);
|
|
205
|
+
```
|
|
150
206
|
|
|
151
207
|
All limiters are in-memory and local to the running process. They do not share
|
|
152
208
|
state across multiple Node.js processes or servers, and state is lost when the
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
FixedWindow: () => FixedWindow,
|
|
24
|
+
LeakyBucket: () => LeakyBucket,
|
|
24
25
|
SlidingWindow: () => SlidingWindow,
|
|
25
26
|
TokenBucket: () => TokenBucket
|
|
26
27
|
});
|
|
@@ -159,9 +160,31 @@ var SlidingWindow = class {
|
|
|
159
160
|
};
|
|
160
161
|
}
|
|
161
162
|
};
|
|
163
|
+
|
|
164
|
+
// src/leaky-bucket.ts
|
|
165
|
+
var LeakyBucket = class {
|
|
166
|
+
constructor(capacity, leakRate) {
|
|
167
|
+
this.capacity = capacity;
|
|
168
|
+
this.queue = [];
|
|
169
|
+
this.leakRate = leakRate;
|
|
170
|
+
}
|
|
171
|
+
addRequest(req) {
|
|
172
|
+
if (this.queue.length >= this.capacity) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
this.queue.push(req);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
leak() {
|
|
179
|
+
if (this.queue.length > 0) {
|
|
180
|
+
this.queue.shift();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
162
184
|
// Annotate the CommonJS export names for ESM import in node:
|
|
163
185
|
0 && (module.exports = {
|
|
164
186
|
FixedWindow,
|
|
187
|
+
LeakyBucket,
|
|
165
188
|
SlidingWindow,
|
|
166
189
|
TokenBucket
|
|
167
190
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -131,8 +131,30 @@ var SlidingWindow = class {
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
};
|
|
134
|
+
|
|
135
|
+
// src/leaky-bucket.ts
|
|
136
|
+
var LeakyBucket = class {
|
|
137
|
+
constructor(capacity, leakRate) {
|
|
138
|
+
this.capacity = capacity;
|
|
139
|
+
this.queue = [];
|
|
140
|
+
this.leakRate = leakRate;
|
|
141
|
+
}
|
|
142
|
+
addRequest(req) {
|
|
143
|
+
if (this.queue.length >= this.capacity) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
this.queue.push(req);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
leak() {
|
|
150
|
+
if (this.queue.length > 0) {
|
|
151
|
+
this.queue.shift();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
134
155
|
export {
|
|
135
156
|
FixedWindow,
|
|
157
|
+
LeakyBucket,
|
|
136
158
|
SlidingWindow,
|
|
137
159
|
TokenBucket
|
|
138
160
|
};
|