Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.

Commit d84f951

Browse files
authored
Merge branch 'main' into 20-use-prepare-workflow-instead-of-publishing-built-js
2 parents bb5d089 + 716289a commit d84f951

File tree

2 files changed

+50
-104
lines changed

2 files changed

+50
-104
lines changed

src/AttemptResult.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,30 @@ import {RateLimit} from "./RateLimit";
22

33
/**
44
* The result from a rate limit attempt
5-
* @interface
65
*/
76
export interface AttemptResult {
87
/**
98
* The number of requests this rate limit allows per time window
10-
* @readonly
119
*/
1210
readonly limit: number;
1311

1412
/**
1513
* The number of requests remaining in the current time window
16-
* @readonly
1714
*/
1815
readonly remaining: number;
1916

2017
/**
2118
* The number of seconds until the current time window resets
22-
* @readonly
2319
*/
2420
readonly reset: number;
2521

2622
/**
2723
* The rate limit that this attempt was made on
28-
* @readonly
2924
*/
3025
readonly rateLimit: RateLimit;
3126

3227
/**
3328
* Whether this attempt should be allowed to proceed. If false, the attempt is rate limited.
34-
* @readonly
3529
*/
3630
readonly allow: boolean;
3731
}

src/RateLimit.ts

Lines changed: 50 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,44 @@ import {AttemptResult} from "./AttemptResult";
22

33
/**
44
* Rate limit
5-
* @class
65
*/
76
export class RateLimit {
87
/**
98
* Rate limit instances
10-
* @private
11-
* @static
12-
* @type {Map<string, RateLimit>}
9+
* @internal
1310
*/
14-
static #instances = new Map<string, RateLimit>();
11+
static readonly #instances = new Map<string, RateLimit>();
1512

1613
/**
1714
* Whether this rate limit is deleted
18-
* @private
19-
* @type {boolean}
15+
* @internal
2016
*/
2117
#deleted = false;
2218

2319
/**
24-
* Attempts memory
25-
* @private
26-
* @type {Map<string, [number, number]>}
20+
* Attempts memory. First number is attempts, second number is timestamp
21+
* @internal
2722
*/
28-
#attempts = new Map<string, [number, number]>();
29-
30-
/**
31-
* Name of the rate limit
32-
* @readonly
33-
* @type {string}
34-
*/
35-
readonly name: string;
36-
/**
37-
* The number of requests allowed per time window
38-
* @type {number}
39-
*/
40-
limit: number;
41-
/**
42-
* The time window in seconds (e.g. 60)
43-
* @type {number}
44-
*/
45-
timeWindow: number;
23+
readonly #attempts = new Map<string, [number, number]>();
4624

4725
/**
4826
* Create a new rate limit
49-
* @param {string} name - The name of the rate limit
50-
* @param {number} limit - The number of requests allowed per time window (e.g. 60)
51-
* @param {number} timeWindow - The time window in seconds (e.g. 60)
52-
* @returns {RateLimit}
27+
* @param name - The name of the rate limit
28+
* @param limit - The number of requests allowed per time window (e.g. 60)
29+
* @param timeWindow - The time window in seconds (e.g. 60)
5330
* @throws {Error} - If the rate limit already exists
5431
*/
55-
constructor(name: string, limit: number, timeWindow: number) {
32+
public constructor(public readonly name: string, public readonly limit: number, public readonly timeWindow: number) {
5633
if (RateLimit.#instances.has(name)) throw new Error(`Rate limit with name "${name}" already exists`);
57-
this.name = name;
58-
this.limit = limit;
59-
this.timeWindow = timeWindow;
6034
RateLimit.#instances.set(name, this);
6135
}
6236

6337
/**
6438
* Check the attempt state for a source ID without decrementing the remaining attempts
65-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
66-
* @param {function(AttemptResult): void} [callback] - Return data in a callback
67-
* @returns {AttemptResult}
39+
* @param source - Unique source identifier (e.g. username, IP, etc.)
40+
* @param [callback] - Return data in a callback
6841
*/
69-
check(source: string, callback?: (result: AttemptResult) => void): AttemptResult {
42+
public check(source: string, callback?: (result: AttemptResult) => void): AttemptResult {
7043
if (this.#deleted) throw new Error(`Rate limit "${this.name}" has been deleted. Construct a new instance`);
7144
const attempts = this.#attempts.get(source) ?? [0, Date.now()];
7245
const remaining = this.limit - attempts[0];
@@ -84,12 +57,11 @@ export class RateLimit {
8457

8558
/**
8659
* Make an attempt with a source ID
87-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
88-
* @param {number} [attempts=1] - The number of attempts to make
89-
* @param {function(AttemptResult): void} [callback] - Return data in a callback
90-
* @returns {AttemptResult}
60+
* @param source - Unique source identifier (e.g. username, IP, etc.)
61+
* @param [attempts=1] - The number of attempts to make
62+
* @param [callback] - Return data in a callback
9163
*/
92-
attempt(source: string, attempts: number = 1, callback?: (result: AttemptResult) => void): AttemptResult {
64+
public attempt(source: string, attempts: number = 1, callback?: (result: AttemptResult) => void): AttemptResult {
9365
if (this.#deleted) throw new Error(`Rate limit "${this.name}" has been deleted. Construct a new instance`);
9466
const data = this.#attempts.get(source) ?? [0, Date.now()];
9567
// if the time window has expired, reset the attempts
@@ -105,22 +77,20 @@ export class RateLimit {
10577

10678
/**
10779
* Reset limit for a source ID. The storage entry will be deleted and a new one will be created on the next attempt.
108-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
109-
* @returns {void}
80+
* @param source - Unique source identifier (e.g. username, IP, etc.)
11081
*/
111-
reset(source: string): void {
82+
public reset(source: string): void {
11283
if (this.#deleted) throw new Error(`Rate limit "${this.name}" has been deleted. Construct a new instance`);
11384
this.#attempts.delete(source);
11485
}
11586

11687
/**
11788
* Set the remaining attempts for a source ID.
11889
* > **Warning**: This is not recommended as the remaining attempts depend on the limit of the instance.
119-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
120-
* @param {number} remaining - The number of remaining attempts
121-
* @returns {void}
90+
* @param source - Unique source identifier (e.g. username, IP, etc.)
91+
* @param remaining - The number of remaining attempts
12292
*/
123-
setRemaining(source: string, remaining: number): void {
93+
public setRemaining(source: string, remaining: number): void {
12494
if (this.#deleted) throw new Error(`Rate limit "${this.name}" has been deleted. Construct a new instance`);
12595
const data = this.#attempts.get(source) ?? [0, Date.now()];
12696
data[0] = this.limit - remaining;
@@ -129,73 +99,63 @@ export class RateLimit {
12999

130100
/**
131101
* Clear rate limit attempts storage. This is equivalent to resetting all rate limits.
132-
* @returns {void}
133102
*/
134-
clear(): void {
103+
public clear(): void {
135104
if (this.#deleted) throw new Error(`Rate limit "${this.name}" has been deleted. Construct a new instance`);
136105
this.#attempts.clear();
137106
}
138107

139108
/**
140109
* Delete the rate limit instance. After it is deleted, it should not be used any further without constructing a new instance.
141-
* @returns {void}
142110
*/
143-
delete(): void {
111+
public delete(): void {
144112
this.clear();
145113
this.#deleted = true;
146114
RateLimit.#instances.delete(this.name);
147115
}
148116

149117
/**
150118
* Get a rate limit instance
151-
* @param {string} name - The name of the rate limit
152-
* @returns {RateLimit | null}
153-
* @static
119+
* @param name - The name of the rate limit
154120
*/
155-
static get(name: string): RateLimit | null {
121+
public static get(name: string): RateLimit | null {
156122
return RateLimit.#instances.get(name) ?? null;
157123
}
158124

159125
/**
160126
* Check the attempt state for a source ID without decrementing the remaining attempts
161-
* @param {string} name - The name of the rate limit
162-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
163-
* @param {function(AttemptResult): void} [callback] - Return data in a callback
164-
* @returns {AttemptResult}
127+
* @param name - The name of the rate limit
128+
* @param source - Unique source identifier (e.g. username, IP, etc.)
129+
* @param [callback] - Return data in a callback
165130
* @throws {Error} - If the rate limit does not exist
166-
* @static
167131
*/
168-
static check(name: string, source: string, callback?: (result: AttemptResult) => void): AttemptResult {
132+
public static check(name: string, source: string, callback?: (result: AttemptResult) => void): AttemptResult {
169133
const rateLimit = RateLimit.get(name);
170134
if (!rateLimit) throw new Error(`Rate limit with name "${name}" does not exist`);
171135
return rateLimit.check(source, callback);
172136
}
173137

174138
/**
175139
* Make an attempt with a source ID
176-
* @param {string} name - The name of the rate limit
177-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
178-
* @param {number} [attempts=1] - The number of attempts to make
179-
* @param {function(AttemptResult): void} [callback] - Return data in a callback
180-
* @returns {AttemptResult}
140+
* @param name - The name of the rate limit
141+
* @param source - Unique source identifier (e.g. username, IP, etc.)
142+
* @param [attempts=1] - The number of attempts to make
143+
* @param [callback] - Return data in a callback
181144
* @throws {Error} - If the rate limit does not exist
182-
* @static
183145
*/
184-
static attempt(name: string, source: string, attempts: number = 1, callback?: (result: AttemptResult) => void): AttemptResult {
146+
public static attempt(name: string, source: string, attempts: number = 1, callback?: (result: AttemptResult) => void): AttemptResult {
185147
const rateLimit = RateLimit.get(name);
186148
if (!rateLimit) throw new Error(`Rate limit with name "${name}" does not exist`);
187149
return rateLimit.attempt(source, attempts, callback);
188150
}
189151

190152
/**
191153
* Reset limit for a source ID. The storage entry will be deleted and a new one will be created on the next attempt.
192-
* @param {string} name - The name of the rate limit
193-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
194-
* @returns {void}
154+
* @param name - The name of the rate limit
155+
* @param source - Unique source identifier (e.g. username, IP, etc.)
195156
* @throws {Error} - If the rate limit does not exist
196-
* @static
197157
*/
198-
static reset(name: string, source: string): void {
158+
public static reset(name: string, source: string): void {
199159
const rateLimit = RateLimit.get(name);
200160
if (!rateLimit) throw new Error(`Rate limit with name "${name}" does not exist`);
201161
return rateLimit.reset(source);
@@ -204,54 +164,46 @@ export class RateLimit {
204164
/**
205165
* Set the remaining attempts for a source ID.
206166
* > **Warning**: This is not recommended as the remaining attempts depend on the limit of the instance.
207-
* @param {string} name - The name of the rate limit
208-
* @param {string} source - Unique source identifier (e.g. username, IP, etc.)
209-
* @param {number} remaining - The number of remaining attempts
210-
* @returns {void}
167+
* @param name - The name of the rate limit
168+
* @param source - Unique source identifier (e.g. username, IP, etc.)
169+
* @param remaining - The number of remaining attempts
211170
* @throws {Error} - If the rate limit does not exist
212-
* @static
213171
*/
214-
static setRemaining(name: string, source: string, remaining: number): void {
172+
public static setRemaining(name: string, source: string, remaining: number): void {
215173
const rateLimit = RateLimit.get(name);
216174
if (!rateLimit) throw new Error(`Rate limit with name "${name}" does not exist`);
217175
return rateLimit.setRemaining(source, remaining);
218176
}
219177

220178
/**
221179
* Clear rate limit attempts storage. This is equivalent to resetting all rate limits.
222-
* @param {string} name - The name of the rate limit
223-
* @returns {void}
180+
* @param name - The name of the rate limit
224181
* @throws {Error} - If the rate limit does not exist
225-
* @static
226182
*/
227-
static clear(name: string): void {
183+
public static clear(name: string): void {
228184
const rateLimit = RateLimit.get(name);
229185
if (!rateLimit) throw new Error(`Rate limit with name "${name}" does not exist`);
230186
return rateLimit.clear();
231187
}
232188

233189
/**
234190
* Delete the rate limit instance. After it is deleted, it should not be used any further without constructing a new instance.
235-
* @param {string} name - The name of the rate limit
236-
* @returns {void}
191+
* @param name - The name of the rate limit
237192
* @throws {Error} - If the rate limit does not exist
238-
* @static
239193
*/
240-
static delete(name: string): void {
194+
public static delete(name: string): void {
241195
const rateLimit = RateLimit.get(name);
242196
if (!rateLimit) throw new Error(`Rate limit with name "${name}" does not exist`);
243197
return rateLimit.delete();
244198
}
245199

246200
/**
247201
* Create a new rate limit
248-
* @param {string} name - The name of the rate limit
249-
* @param {number} limit - The number of attempts allowed per time window (e.g. 60)
250-
* @param {number} timeWindow - The time window in seconds (e.g. 60)
251-
* @returns {RateLimit}
252-
* @static
202+
* @param name - The name of the rate limit
203+
* @param limit - The number of attempts allowed per time window (e.g. 60)
204+
* @param timeWindow - The time window in seconds (e.g. 60)
253205
*/
254-
static create(name: string, limit: number, timeWindow: number): RateLimit {
206+
public static create(name: string, limit: number, timeWindow: number): RateLimit {
255207
const existing = RateLimit.get(name);
256208
if (existing) return existing;
257209
return new RateLimit(name, limit, timeWindow);

0 commit comments

Comments
 (0)