@atproto/xrpc-server 0.11.0-next.0 → 0.11.1
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/CHANGELOG.md +30 -16
- package/dist/rate-limiter-http.d.ts +24 -0
- package/dist/rate-limiter-http.d.ts.map +1 -0
- package/dist/rate-limiter-http.js +49 -0
- package/dist/rate-limiter-http.js.map +1 -0
- package/dist/rate-limiter.d.ts +32 -42
- package/dist/rate-limiter.d.ts.map +1 -1
- package/dist/rate-limiter.js +29 -75
- package/dist/rate-limiter.js.map +1 -1
- package/dist/server.d.ts +4 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +26 -7
- package/dist/server.js.map +1 -1
- package/jest.config.cjs +9 -2
- package/package.json +14 -14
- package/src/rate-limiter-http.ts +82 -0
- package/src/rate-limiter.ts +68 -156
- package/src/server.ts +49 -13
- package/tests/auth.test.ts +4 -4
- package/tsconfig.build.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,32 +1,46 @@
|
|
|
1
1
|
# @atproto/xrpc-server
|
|
2
2
|
|
|
3
|
-
## 0.11.
|
|
3
|
+
## 0.11.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4969](https://github.com/bluesky-social/atproto/pull/4969) [`57ecbda`](https://github.com/bluesky-social/atproto/commit/57ecbdac3eb14fdbc853456371e84a5098e91c4b) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Uses the http res's log Pino logger upon rate limit computation error, allowing to log the failure in the context of an http request
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`482767c`](https://github.com/bluesky-social/atproto/commit/482767c4bcce95aa390b2992b028fd8e27d162b2)]:
|
|
10
|
+
- @atproto/lex-client@0.1.2
|
|
11
|
+
|
|
12
|
+
## 0.11.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
|
6
15
|
|
|
7
|
-
- [#4929](https://github.com/bluesky-social/atproto/pull/4929) [`
|
|
16
|
+
- [#4929](https://github.com/bluesky-social/atproto/pull/4929) [`f01c59f`](https://github.com/bluesky-social/atproto/commit/f01c59f5bd3f75fb8b47a9eecd4858b84033fb7c) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Drop support for Node.js 18 and 20. Node.js 22 is now the minimum supported version. Docker images now use Node.js 24.
|
|
8
17
|
|
|
9
|
-
- [#4943](https://github.com/bluesky-social/atproto/pull/4943) [`
|
|
18
|
+
- [#4943](https://github.com/bluesky-social/atproto/pull/4943) [`c459153`](https://github.com/bluesky-social/atproto/commit/c459153395a30ce89e050892c8fab7dc98e019b9) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Convert to pure ESM. All packages now ship `"type": "module"` with ES module output and Node16 module resolution.
|
|
10
19
|
|
|
11
20
|
Node.js 22's `require()` compatibility layer can still load these packages in CommonJS code.
|
|
12
21
|
|
|
13
|
-
- [#4930](https://github.com/bluesky-social/atproto/pull/4930) [`
|
|
22
|
+
- [#4930](https://github.com/bluesky-social/atproto/pull/4930) [`908bece`](https://github.com/bluesky-social/atproto/commit/908bece169258bff5ad121e5eec157d6ded6f705) Thanks [@devinivy](https://github.com/devinivy)! - Build with TypeScript 6.0.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [[`affb50c`](https://github.com/bluesky-social/atproto/commit/affb50c040b497a12631df99a6310f8e78cab557), [`f01c59f`](https://github.com/bluesky-social/atproto/commit/f01c59f5bd3f75fb8b47a9eecd4858b84033fb7c), [`c459153`](https://github.com/bluesky-social/atproto/commit/c459153395a30ce89e050892c8fab7dc98e019b9), [`affb50c`](https://github.com/bluesky-social/atproto/commit/affb50c040b497a12631df99a6310f8e78cab557), [`908bece`](https://github.com/bluesky-social/atproto/commit/908bece169258bff5ad121e5eec157d6ded6f705)]:
|
|
27
|
+
- @atproto/common@0.6.0
|
|
28
|
+
- @atproto/crypto@0.5.0
|
|
29
|
+
- @atproto/lex-cbor@0.1.0
|
|
30
|
+
- @atproto/lex-client@0.1.0
|
|
31
|
+
- @atproto/lex-data@0.1.0
|
|
32
|
+
- @atproto/lex-json@0.1.0
|
|
33
|
+
- @atproto/lex-schema@0.1.0
|
|
34
|
+
- @atproto/lexicon@0.7.0
|
|
35
|
+
- @atproto/ws-client@0.1.0
|
|
36
|
+
- @atproto/xrpc@0.8.0
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
## 0.10.22
|
|
16
39
|
|
|
17
40
|
### Patch Changes
|
|
18
41
|
|
|
19
|
-
- Updated dependencies [[`
|
|
20
|
-
- @atproto/
|
|
21
|
-
- @atproto/crypto@0.5.0-next.0
|
|
22
|
-
- @atproto/lex-cbor@0.1.0-next.0
|
|
23
|
-
- @atproto/lex-client@0.1.0-next.0
|
|
24
|
-
- @atproto/lex-data@0.1.0-next.0
|
|
25
|
-
- @atproto/lex-json@0.1.0-next.0
|
|
26
|
-
- @atproto/lex-schema@0.1.0-next.0
|
|
27
|
-
- @atproto/lexicon@0.7.0-next.0
|
|
28
|
-
- @atproto/ws-client@0.1.0-next.0
|
|
29
|
-
- @atproto/xrpc@0.8.0-next.0
|
|
42
|
+
- Updated dependencies [[`d8b2374`](https://github.com/bluesky-social/atproto/commit/d8b2374e1592d1dec65a33439791bc141f02397a)]:
|
|
43
|
+
- @atproto/lex-client@0.0.22
|
|
30
44
|
|
|
31
45
|
## 0.10.21
|
|
32
46
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { RateLimiterConsume, RateLimiterI, RateLimiterReset, RateLimiterStatus } from './rate-limiter.js';
|
|
3
|
+
export interface HttpRateLimiterContext {
|
|
4
|
+
req: IncomingMessage;
|
|
5
|
+
res?: ServerResponse;
|
|
6
|
+
}
|
|
7
|
+
export type HttpRateLimiterOptions<C extends HttpRateLimiterContext = HttpRateLimiterContext> = {
|
|
8
|
+
bypass?: (ctx: C) => boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Wraps a {@link RateLimiterI} class with an {@link RateLimiterI}
|
|
12
|
+
* implementation that will apply the appropriate headers to the response if a
|
|
13
|
+
* limit is exceeded.
|
|
14
|
+
*/
|
|
15
|
+
export declare class HttpRateLimiter<C extends HttpRateLimiterContext = HttpRateLimiterContext> implements RateLimiterI<C> {
|
|
16
|
+
private readonly rateLimiter;
|
|
17
|
+
private readonly options;
|
|
18
|
+
constructor(rateLimiter: RateLimiterI<C>, options?: Readonly<HttpRateLimiterOptions<C>>);
|
|
19
|
+
handle(ctx: C): Promise<void>;
|
|
20
|
+
consume(...args: Parameters<RateLimiterConsume<C>>): Promise<RateLimiterStatus | null>;
|
|
21
|
+
reset(...args: Parameters<RateLimiterReset<C>>): Promise<void>;
|
|
22
|
+
static from<C extends HttpRateLimiterContext = HttpRateLimiterContext>(rateLimiters: readonly RateLimiterI<C>[], { bypass }?: HttpRateLimiterOptions<C>): HttpRateLimiter<C> | undefined;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=rate-limiter-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter-http.d.ts","sourceRoot":"","sources":["../src/rate-limiter-http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EAGL,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,mBAAmB,CAAA;AAE1B,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,eAAe,CAAA;IACpB,GAAG,CAAC,EAAE,cAAc,CAAA;CACrB;AAED,MAAM,MAAM,sBAAsB,CAChC,CAAC,SAAS,sBAAsB,GAAG,sBAAsB,IACvD;IACF,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAA;CAC7B,CAAA;AAED;;;;GAIG;AACH,qBAAa,eAAe,CAC1B,CAAC,SAAS,sBAAsB,GAAG,sBAAsB,CACzD,YAAW,YAAY,CAAC,CAAC,CAAC;IAGxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,EAC5B,OAAO,GAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAM;IAG9D,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7B,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAIlD,KAAK,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAIpD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,sBAAsB,GAAG,sBAAsB,EACnE,YAAY,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,EACxC,EAAE,MAAM,EAAE,GAAE,sBAAsB,CAAC,CAAC,CAAM,GACzC,eAAe,CAAC,CAAC,CAAC,GAAG,SAAS;CAMlC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { CombinedRateLimiter, RateLimitExceededError, } from './rate-limiter.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a {@link RateLimiterI} class with an {@link RateLimiterI}
|
|
4
|
+
* implementation that will apply the appropriate headers to the response if a
|
|
5
|
+
* limit is exceeded.
|
|
6
|
+
*/
|
|
7
|
+
export class HttpRateLimiter {
|
|
8
|
+
constructor(rateLimiter, options = {}) {
|
|
9
|
+
this.rateLimiter = rateLimiter;
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
async handle(ctx) {
|
|
13
|
+
const { bypass } = this.options;
|
|
14
|
+
if (bypass && bypass(ctx))
|
|
15
|
+
return;
|
|
16
|
+
try {
|
|
17
|
+
const result = await this.consume(ctx);
|
|
18
|
+
if (result != null) {
|
|
19
|
+
setStatusHeaders(ctx, result);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
if (err instanceof RateLimitExceededError) {
|
|
24
|
+
setStatusHeaders(ctx, err.status);
|
|
25
|
+
}
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async consume(...args) {
|
|
30
|
+
return this.rateLimiter.consume(...args);
|
|
31
|
+
}
|
|
32
|
+
async reset(...args) {
|
|
33
|
+
return this.rateLimiter.reset(...args);
|
|
34
|
+
}
|
|
35
|
+
static from(rateLimiters, { bypass } = {}) {
|
|
36
|
+
const rateLimiter = CombinedRateLimiter.from(rateLimiters);
|
|
37
|
+
if (!rateLimiter)
|
|
38
|
+
return undefined;
|
|
39
|
+
return new HttpRateLimiter(rateLimiter, { bypass });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function setStatusHeaders(ctx, status) {
|
|
43
|
+
const resetAt = Math.floor((Date.now() + status.msBeforeNext) / 1e3);
|
|
44
|
+
ctx.res?.setHeader('RateLimit-Limit', status.limit);
|
|
45
|
+
ctx.res?.setHeader('RateLimit-Reset', resetAt);
|
|
46
|
+
ctx.res?.setHeader('RateLimit-Remaining', status.remainingPoints);
|
|
47
|
+
ctx.res?.setHeader('RateLimit-Policy', `${status.limit};w=${status.duration}`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=rate-limiter-http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter-http.js","sourceRoot":"","sources":["../src/rate-limiter-http.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,sBAAsB,GAKvB,MAAM,mBAAmB,CAAA;AAa1B;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAI1B,YACmB,WAA4B,EAC5B,UAA+C,EAAE;QADjD,gBAAW,GAAX,WAAW,CAAiB;QAC5B,YAAO,GAAP,OAAO,CAA0C;IACjE,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAC,GAAM;QACjB,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAA;QAC/B,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC;YAAE,OAAM;QAEjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACtC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;gBACnB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YAC/B,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,sBAAsB,EAAE,CAAC;gBAC1C,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;YACnC,CAAC;YAED,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAG,IAAuC;QACtD,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAG,IAAqC;QAClD,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,CAAC,IAAI,CACT,YAAwC,EACxC,EAAE,MAAM,KAAgC,EAAE;QAE1C,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC1D,IAAI,CAAC,WAAW;YAAE,OAAO,SAAS,CAAA;QAElC,OAAO,IAAI,eAAe,CAAI,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IACxD,CAAC;CACF;AAED,SAAS,gBAAgB,CAEvB,GAAM,EAAE,MAAyB;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,CAAA;IAEpE,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;IACnD,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAA;IAC9C,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,qBAAqB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IACjE,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,kBAAkB,EAAE,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;AAChF,CAAC","sourcesContent":["import { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n CombinedRateLimiter,\n RateLimitExceededError,\n RateLimiterConsume,\n RateLimiterI,\n RateLimiterReset,\n RateLimiterStatus,\n} from './rate-limiter.js'\n\nexport interface HttpRateLimiterContext {\n req: IncomingMessage\n res?: ServerResponse\n}\n\nexport type HttpRateLimiterOptions<\n C extends HttpRateLimiterContext = HttpRateLimiterContext,\n> = {\n bypass?: (ctx: C) => boolean\n}\n\n/**\n * Wraps a {@link RateLimiterI} class with an {@link RateLimiterI}\n * implementation that will apply the appropriate headers to the response if a\n * limit is exceeded.\n */\nexport class HttpRateLimiter<\n C extends HttpRateLimiterContext = HttpRateLimiterContext,\n> implements RateLimiterI<C>\n{\n constructor(\n private readonly rateLimiter: RateLimiterI<C>,\n private readonly options: Readonly<HttpRateLimiterOptions<C>> = {},\n ) {}\n\n async handle(ctx: C): Promise<void> {\n const { bypass } = this.options\n if (bypass && bypass(ctx)) return\n\n try {\n const result = await this.consume(ctx)\n if (result != null) {\n setStatusHeaders(ctx, result)\n }\n } catch (err) {\n if (err instanceof RateLimitExceededError) {\n setStatusHeaders(ctx, err.status)\n }\n\n throw err\n }\n }\n\n async consume(...args: Parameters<RateLimiterConsume<C>>) {\n return this.rateLimiter.consume(...args)\n }\n\n async reset(...args: Parameters<RateLimiterReset<C>>) {\n return this.rateLimiter.reset(...args)\n }\n\n static from<C extends HttpRateLimiterContext = HttpRateLimiterContext>(\n rateLimiters: readonly RateLimiterI<C>[],\n { bypass }: HttpRateLimiterOptions<C> = {},\n ): HttpRateLimiter<C> | undefined {\n const rateLimiter = CombinedRateLimiter.from(rateLimiters)\n if (!rateLimiter) return undefined\n\n return new HttpRateLimiter<C>(rateLimiter, { bypass })\n }\n}\n\nfunction setStatusHeaders<\n C extends HttpRateLimiterContext = HttpRateLimiterContext,\n>(ctx: C, status: RateLimiterStatus) {\n const resetAt = Math.floor((Date.now() + status.msBeforeNext) / 1e3)\n\n ctx.res?.setHeader('RateLimit-Limit', status.limit)\n ctx.res?.setHeader('RateLimit-Reset', resetAt)\n ctx.res?.setHeader('RateLimit-Remaining', status.remainingPoints)\n ctx.res?.setHeader('RateLimit-Policy', `${status.limit};w=${status.duration}`)\n}\n"]}
|
package/dist/rate-limiter.d.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
1
|
import { RateLimiterAbstract, RateLimiterRes } from 'rate-limiter-flexible';
|
|
3
2
|
import { XRPCError } from './errors.js';
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export type CalcKeyFn<C extends RateLimiterContext = RateLimiterContext> = (ctx: C) => string | null;
|
|
9
|
-
export type CalcPointsFn<C extends RateLimiterContext = RateLimiterContext> = (ctx: C) => number;
|
|
10
|
-
export interface RateLimiterI<C extends RateLimiterContext = RateLimiterContext> {
|
|
3
|
+
export type { RateLimiterAbstract };
|
|
4
|
+
export type CalcKeyFn<C = unknown> = (ctx: C) => string | null;
|
|
5
|
+
export type CalcPointsFn<C = unknown> = (ctx: C) => number;
|
|
6
|
+
export interface RateLimiterI<C = unknown> {
|
|
11
7
|
consume: RateLimiterConsume<C>;
|
|
12
8
|
reset: RateLimiterReset<C>;
|
|
13
9
|
}
|
|
14
|
-
export type RateLimiterConsumeOptions<C
|
|
10
|
+
export type RateLimiterConsumeOptions<C = unknown> = {
|
|
15
11
|
calcKey?: CalcKeyFn<C>;
|
|
16
12
|
calcPoints?: CalcPointsFn<C>;
|
|
17
13
|
};
|
|
18
|
-
export type RateLimiterConsume<C
|
|
14
|
+
export type RateLimiterConsume<C = unknown> = (ctx: C, opts?: RateLimiterConsumeOptions<C>) => Promise<RateLimiterStatus | null>;
|
|
19
15
|
export type RateLimiterStatus = {
|
|
20
16
|
limit: number;
|
|
21
17
|
duration: number;
|
|
@@ -24,35 +20,45 @@ export type RateLimiterStatus = {
|
|
|
24
20
|
consumedPoints: number;
|
|
25
21
|
isFirstInDuration: boolean;
|
|
26
22
|
};
|
|
27
|
-
export type RateLimiterResetOptions<C
|
|
23
|
+
export type RateLimiterResetOptions<C = unknown> = {
|
|
28
24
|
calcKey?: CalcKeyFn<C>;
|
|
29
25
|
};
|
|
30
|
-
export type RateLimiterReset<C
|
|
31
|
-
export type
|
|
26
|
+
export type RateLimiterReset<C = unknown> = (ctx: C, opts?: RateLimiterResetOptions<C>) => Promise<void>;
|
|
27
|
+
export type RateLimiterErrorHandlerDetails = {
|
|
28
|
+
key: string;
|
|
29
|
+
points: number;
|
|
30
|
+
limiter: {
|
|
31
|
+
keyPrefix: string;
|
|
32
|
+
points: number;
|
|
33
|
+
duration: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export type RateLimiterErrorHandler<C = unknown> = (err: unknown, ctx: C, details: RateLimiterErrorHandlerDetails) => Promise<RateLimiterStatus | null>;
|
|
37
|
+
export type RateLimiterOptions<C = unknown> = {
|
|
32
38
|
keyPrefix: string;
|
|
33
39
|
durationMs: number;
|
|
34
40
|
points: number;
|
|
35
41
|
calcKey: CalcKeyFn<C>;
|
|
36
42
|
calcPoints: CalcPointsFn<C>;
|
|
37
|
-
|
|
43
|
+
onError?: RateLimiterErrorHandler<C>;
|
|
38
44
|
};
|
|
39
|
-
export declare class RateLimiter<C
|
|
45
|
+
export declare class RateLimiter<C = unknown> implements RateLimiterI<C> {
|
|
40
46
|
limiter: RateLimiterAbstract;
|
|
41
|
-
private readonly
|
|
47
|
+
private readonly onError?;
|
|
42
48
|
private readonly calcKey;
|
|
43
49
|
private readonly calcPoints;
|
|
44
50
|
constructor(limiter: RateLimiterAbstract, options: RateLimiterOptions<C>);
|
|
45
|
-
consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus |
|
|
51
|
+
consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus | null>;
|
|
46
52
|
reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void>;
|
|
47
53
|
}
|
|
48
|
-
export declare class MemoryRateLimiter<C
|
|
54
|
+
export declare class MemoryRateLimiter<C = unknown> extends RateLimiter<C> {
|
|
49
55
|
constructor(options: RateLimiterOptions<C>);
|
|
50
56
|
}
|
|
51
|
-
export declare class RedisRateLimiter<C
|
|
57
|
+
export declare class RedisRateLimiter<C = unknown> extends RateLimiter<C> {
|
|
52
58
|
constructor(storeClient: unknown, options: RateLimiterOptions<C>);
|
|
53
59
|
}
|
|
54
60
|
export declare const formatLimiterStatus: (limiter: RateLimiterAbstract, res: RateLimiterRes) => RateLimiterStatus;
|
|
55
|
-
export type WrappedRateLimiterOptions<C
|
|
61
|
+
export type WrappedRateLimiterOptions<C = unknown> = {
|
|
56
62
|
calcKey?: CalcKeyFn<C>;
|
|
57
63
|
calcPoints?: CalcPointsFn<C>;
|
|
58
64
|
};
|
|
@@ -60,13 +66,13 @@ export type WrappedRateLimiterOptions<C extends RateLimiterContext = RateLimiter
|
|
|
60
66
|
* Wraps a {@link RateLimiterI} instance with custom key and points calculation
|
|
61
67
|
* functions.
|
|
62
68
|
*/
|
|
63
|
-
export declare class WrappedRateLimiter<C
|
|
69
|
+
export declare class WrappedRateLimiter<C = unknown> implements RateLimiterI<C> {
|
|
64
70
|
private readonly rateLimiter;
|
|
65
71
|
private readonly options;
|
|
66
72
|
private constructor();
|
|
67
|
-
consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus |
|
|
73
|
+
consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus | null>;
|
|
68
74
|
reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void>;
|
|
69
|
-
static from<C
|
|
75
|
+
static from<C = unknown>(rateLimiter: RateLimiterI<C>, { calcKey, calcPoints }?: WrappedRateLimiterOptions<C>): RateLimiterI<C>;
|
|
70
76
|
}
|
|
71
77
|
/**
|
|
72
78
|
* Combines multiple rate limiters into one.
|
|
@@ -74,28 +80,12 @@ export declare class WrappedRateLimiter<C extends RateLimiterContext = RateLimit
|
|
|
74
80
|
* The combined rate limiter will return the tightest (most restrictive) of all
|
|
75
81
|
* the provided rate limiters.
|
|
76
82
|
*/
|
|
77
|
-
export declare class CombinedRateLimiter<C
|
|
83
|
+
export declare class CombinedRateLimiter<C = unknown> implements RateLimiterI<C> {
|
|
78
84
|
private readonly rateLimiters;
|
|
79
85
|
private constructor();
|
|
80
|
-
consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus |
|
|
86
|
+
consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus | null>;
|
|
81
87
|
reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void>;
|
|
82
|
-
static from<C
|
|
83
|
-
}
|
|
84
|
-
export type RouteRateLimiterOptions<C extends RateLimiterContext = RateLimiterContext> = {
|
|
85
|
-
bypass?: (ctx: C) => boolean;
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* Wraps a {@link RateLimiterI} interface into a class that will apply the
|
|
89
|
-
* appropriate headers to the response if a limit is exceeded.
|
|
90
|
-
*/
|
|
91
|
-
export declare class RouteRateLimiter<C extends RateLimiterContext = RateLimiterContext> implements RateLimiterI<C> {
|
|
92
|
-
private readonly rateLimiter;
|
|
93
|
-
private readonly options;
|
|
94
|
-
constructor(rateLimiter: RateLimiterI<C>, options?: Readonly<RouteRateLimiterOptions<C>>);
|
|
95
|
-
handle(ctx: C): Promise<RateLimiterStatus | null>;
|
|
96
|
-
consume(...args: Parameters<RateLimiterConsume<C>>): Promise<RateLimiterStatus | RateLimitExceededError | null>;
|
|
97
|
-
reset(...args: Parameters<RateLimiterReset<C>>): Promise<void>;
|
|
98
|
-
static from<C extends RateLimiterContext = RateLimiterContext>(rateLimiters: readonly RateLimiterI<C>[], { bypass }?: RouteRateLimiterOptions<C>): RouteRateLimiter<C> | undefined;
|
|
88
|
+
static from<C = unknown>(rateLimiters: readonly RateLimiterI<C>[]): RateLimiterI<C> | undefined;
|
|
99
89
|
}
|
|
100
90
|
export declare class RateLimitExceededError extends XRPCError {
|
|
101
91
|
status: RateLimiterStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
|
package/dist/rate-limiter.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { RateLimiterMemory, RateLimiterRedis, RateLimiterRes, } from 'rate-limiter-flexible';
|
|
2
2
|
import { ResponseType, XRPCError } from './errors.js';
|
|
3
|
-
import { logger } from './logger.js';
|
|
4
3
|
export class RateLimiter {
|
|
5
4
|
constructor(limiter, options) {
|
|
6
5
|
this.limiter = limiter;
|
|
7
6
|
this.limiter = limiter;
|
|
8
|
-
this.
|
|
7
|
+
this.onError = options.onError;
|
|
9
8
|
this.calcKey = options.calcKey;
|
|
10
9
|
this.calcPoints = options.calcPoints;
|
|
11
10
|
}
|
|
@@ -20,27 +19,29 @@ export class RateLimiter {
|
|
|
20
19
|
if (points < 1) {
|
|
21
20
|
return null;
|
|
22
21
|
}
|
|
22
|
+
const { limiter } = this;
|
|
23
23
|
try {
|
|
24
|
-
const res = await
|
|
25
|
-
return formatLimiterStatus(
|
|
24
|
+
const res = await limiter.consume(key, points);
|
|
25
|
+
return formatLimiterStatus(limiter, res);
|
|
26
26
|
}
|
|
27
27
|
catch (err) {
|
|
28
|
-
// yes this library rejects with a res not an error
|
|
29
28
|
if (err instanceof RateLimiterRes) {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
// Wrap rate-limiter-flexible error into our own error type
|
|
30
|
+
const status = formatLimiterStatus(limiter, err);
|
|
31
|
+
throw new RateLimitExceededError(status);
|
|
32
|
+
}
|
|
33
|
+
else if (err instanceof RateLimitExceededError) {
|
|
34
|
+
// Propagate RateLimitExceededError errors
|
|
35
|
+
throw err;
|
|
32
36
|
}
|
|
33
37
|
else {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
duration: this.limiter.duration,
|
|
42
|
-
}, 'rate limiter failed to consume points');
|
|
43
|
-
return null;
|
|
38
|
+
// Most likely a system error (failed to connect to Redis, etc). Allow
|
|
39
|
+
// the caller to decide how to handle it (fail open by allowing the
|
|
40
|
+
// request, fail closed by rejecting the request, etc).
|
|
41
|
+
const { onError } = this;
|
|
42
|
+
if (onError)
|
|
43
|
+
return onError(err, ctx, { key, points, limiter });
|
|
44
|
+
throw err;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -128,7 +129,17 @@ export class CombinedRateLimiter {
|
|
|
128
129
|
const promises = [];
|
|
129
130
|
for (const rl of this.rateLimiters)
|
|
130
131
|
promises.push(rl.consume(ctx, opts));
|
|
131
|
-
|
|
132
|
+
const results = await Promise.all(promises);
|
|
133
|
+
// Compute the tightest rate limit status (the one with the least remaining points)
|
|
134
|
+
let lowest = null;
|
|
135
|
+
for (const resp of results) {
|
|
136
|
+
if (resp === null)
|
|
137
|
+
continue;
|
|
138
|
+
if (lowest === null || resp.remainingPoints < lowest.remainingPoints) {
|
|
139
|
+
lowest = resp;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return lowest;
|
|
132
143
|
}
|
|
133
144
|
async reset(ctx, opts) {
|
|
134
145
|
const promises = [];
|
|
@@ -144,63 +155,6 @@ export class CombinedRateLimiter {
|
|
|
144
155
|
return new CombinedRateLimiter(rateLimiters);
|
|
145
156
|
}
|
|
146
157
|
}
|
|
147
|
-
const getTightestLimit = (resps) => {
|
|
148
|
-
let lowest = null;
|
|
149
|
-
for (const resp of resps) {
|
|
150
|
-
if (resp === null)
|
|
151
|
-
continue;
|
|
152
|
-
if (resp instanceof RateLimitExceededError)
|
|
153
|
-
return resp;
|
|
154
|
-
if (lowest === null || resp.remainingPoints < lowest.remainingPoints) {
|
|
155
|
-
lowest = resp;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return lowest;
|
|
159
|
-
};
|
|
160
|
-
/**
|
|
161
|
-
* Wraps a {@link RateLimiterI} interface into a class that will apply the
|
|
162
|
-
* appropriate headers to the response if a limit is exceeded.
|
|
163
|
-
*/
|
|
164
|
-
export class RouteRateLimiter {
|
|
165
|
-
constructor(rateLimiter, options = {}) {
|
|
166
|
-
this.rateLimiter = rateLimiter;
|
|
167
|
-
this.options = options;
|
|
168
|
-
}
|
|
169
|
-
async handle(ctx) {
|
|
170
|
-
const { bypass } = this.options;
|
|
171
|
-
if (bypass && bypass(ctx)) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
const result = await this.consume(ctx);
|
|
175
|
-
if (result instanceof RateLimitExceededError) {
|
|
176
|
-
setStatusHeaders(ctx, result.status);
|
|
177
|
-
throw result;
|
|
178
|
-
}
|
|
179
|
-
else if (result != null) {
|
|
180
|
-
setStatusHeaders(ctx, result);
|
|
181
|
-
}
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
async consume(...args) {
|
|
185
|
-
return this.rateLimiter.consume(...args);
|
|
186
|
-
}
|
|
187
|
-
async reset(...args) {
|
|
188
|
-
return this.rateLimiter.reset(...args);
|
|
189
|
-
}
|
|
190
|
-
static from(rateLimiters, { bypass } = {}) {
|
|
191
|
-
const rateLimiter = CombinedRateLimiter.from(rateLimiters);
|
|
192
|
-
if (!rateLimiter)
|
|
193
|
-
return undefined;
|
|
194
|
-
return new RouteRateLimiter(rateLimiter, { bypass });
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function setStatusHeaders(ctx, status) {
|
|
198
|
-
const resetAt = Math.floor((Date.now() + status.msBeforeNext) / 1e3);
|
|
199
|
-
ctx.res?.setHeader('RateLimit-Limit', status.limit);
|
|
200
|
-
ctx.res?.setHeader('RateLimit-Reset', resetAt);
|
|
201
|
-
ctx.res?.setHeader('RateLimit-Remaining', status.remainingPoints);
|
|
202
|
-
ctx.res?.setHeader('RateLimit-Policy', `${status.limit};w=${status.duration}`);
|
|
203
|
-
}
|
|
204
158
|
export class RateLimitExceededError extends XRPCError {
|
|
205
159
|
constructor(status, errorMessage, customErrorName, options) {
|
|
206
160
|
super(ResponseType.RateLimitExceeded, errorMessage, customErrorName, options);
|
package/dist/rate-limiter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAoEpC,MAAM,OAAO,WAAW;IAOtB,YACS,OAA4B,EACnC,OAA8B;QADvB,YAAO,GAAP,OAAO,CAAqB;QAGnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAA;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;QAC9B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA;IACtC,CAAC;IAED,KAAK,CAAC,OAAO,CACX,GAAM,EACN,IAAmC;QAEnC,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAA;QAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QACxB,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,IAAI,CAAC,UAAU,CAAA;QACtD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YACnD,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mDAAmD;YACnD,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;gBAClC,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;gBACrD,OAAO,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAA;YAC3C,CAAC;iBAAM,CAAC;gBACN,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpB,MAAM,GAAG,CAAA;gBACX,CAAC;gBACD,MAAM,CAAC,KAAK,CACV;oBACE,GAAG;oBACH,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;oBACjC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;oBAC3B,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;iBAChC,EACD,uCAAuC,CACxC,CAAA;gBACD,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAM,EAAE,IAAiC;QACnD,MAAM,GAAG,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACjE,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,OAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QACxE,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,iBAEX,SAAQ,WAAc;IACtB,YAAY,OAA8B;QACxC,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC;YACpC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAA;QACF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACzB,CAAC;CACF;AAED,MAAM,OAAO,gBAEX,SAAQ,WAAc;IACtB,YAAY,WAAoB,EAAE,OAA8B;QAC9D,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;YACnC,WAAW;YACX,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAA;QACF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACzB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAA4B,EAC5B,GAAmB,EACA,EAAE;IACrB,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,MAAM;QACrB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;KACzC,CAAA;AACH,CAAC,CAAA;AASD;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IAI7B,YACmB,WAA4B,EAC5B,OAA+C;QAD/C,gBAAW,GAAX,WAAW,CAAiB;QAC5B,YAAO,GAAP,OAAO,CAAwC;IAC/D,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAM,EAAE,IAAmC;QACvD,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE;YACnC,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;YAC9C,UAAU,EAAE,IAAI,EAAE,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU;SACxD,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAM,EAAE,IAAiC;QACnD,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE;YACjC,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;SAC/C,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,IAAI,CACT,WAA4B,EAC5B,EAAE,OAAO,EAAE,UAAU,KAAmC,EAAE;QAE1D,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU;YAAE,OAAO,WAAW,CAAA;QAC/C,OAAO,IAAI,kBAAkB,CAAI,WAAW,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAA;IACxE,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,mBAAmB;IAI9B,YACmB,YAAwC;QAAxC,iBAAY,GAAZ,YAAY,CAA4B;IACxD,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAM,EAAE,IAAmC;QACvD,MAAM,QAAQ,GAAqC,EAAE,CAAA;QACrD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,YAAY;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;QACxE,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IACrD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAM,EAAE,IAAiC;QACnD,MAAM,QAAQ,GAAmC,EAAE,CAAA;QACnD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,YAAY;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;QACtE,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;IAED,MAAM,CAAC,IAAI,CACT,YAAwC;QAExC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAC/C,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,YAAY,CAAC,CAAC,CAAC,CAAA;QACrD,OAAO,IAAI,mBAAmB,CAAC,YAAY,CAAC,CAAA;IAC9C,CAAC;CACF;AAED,MAAM,gBAAgB,GAAG,CACvB,KAA4D,EACT,EAAE;IACrD,IAAI,MAAM,GAA6B,IAAI,CAAA;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,IAAI;YAAE,SAAQ;QAC3B,IAAI,IAAI,YAAY,sBAAsB;YAAE,OAAO,IAAI,CAAA;QACvD,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;YACrE,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAQD;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IAG3B,YACmB,WAA4B,EAC5B,UAAgD,EAAE;QADlD,gBAAW,GAAX,WAAW,CAAiB;QAC5B,YAAO,GAAP,OAAO,CAA2C;IAClE,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAC,GAAM;QACjB,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAA;QAC/B,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAA;QACb,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,MAAM,YAAY,sBAAsB,EAAE,CAAC;YAC7C,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YACpC,MAAM,MAAM,CAAA;QACd,CAAC;aAAM,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YAC1B,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC/B,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAG,IAAuC;QACtD,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAG,IAAqC;QAClD,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,CAAC,IAAI,CACT,YAAwC,EACxC,EAAE,MAAM,KAAiC,EAAE;QAE3C,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC1D,IAAI,CAAC,WAAW;YAAE,OAAO,SAAS,CAAA;QAElC,OAAO,IAAI,gBAAgB,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IACtD,CAAC;CACF;AAED,SAAS,gBAAgB,CACvB,GAAM,EACN,MAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,CAAA;IAEpE,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;IACnD,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAA;IAC9C,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,qBAAqB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IACjE,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,kBAAkB,EAAE,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;AAChF,CAAC;AAED,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IACnD,YACS,MAAyB,EAChC,YAAqB,EACrB,eAAwB,EACxB,OAAsB;QAEtB,KAAK,CACH,YAAY,CAAC,iBAAiB,EAC9B,YAAY,EACZ,eAAe,EACf,OAAO,CACR,CAAA;QAVM,WAAM,GAAN,MAAM,CAAmB;IAWlC,CAAC;IAED,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAiB;QACpC,OAAO,CACL,QAAQ,YAAY,SAAS;YAC7B,QAAQ,CAAC,IAAI,KAAK,YAAY,CAAC,iBAAiB,CACjD,CAAA;IACH,CAAC;CACF","sourcesContent":["import { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n RateLimiterAbstract,\n RateLimiterMemory,\n RateLimiterRedis,\n RateLimiterRes,\n} from 'rate-limiter-flexible'\nimport { ResponseType, XRPCError } from './errors.js'\nimport { logger } from './logger.js'\n\n// @NOTE Do not depend (directly or indirectly) on \"./types\" here, as it would\n// create a circular dependency.\n\nexport interface RateLimiterContext {\n req: IncomingMessage\n res?: ServerResponse\n}\n\nexport type CalcKeyFn<C extends RateLimiterContext = RateLimiterContext> = (\n ctx: C,\n) => string | null\nexport type CalcPointsFn<C extends RateLimiterContext = RateLimiterContext> = (\n ctx: C,\n) => number\n\nexport interface RateLimiterI<\n C extends RateLimiterContext = RateLimiterContext,\n> {\n consume: RateLimiterConsume<C>\n reset: RateLimiterReset<C>\n}\n\nexport type RateLimiterConsumeOptions<\n C extends RateLimiterContext = RateLimiterContext,\n> = {\n calcKey?: CalcKeyFn<C>\n calcPoints?: CalcPointsFn<C>\n}\n\nexport type RateLimiterConsume<\n C extends RateLimiterContext = RateLimiterContext,\n> = (\n ctx: C,\n opts?: RateLimiterConsumeOptions<C>,\n) => Promise<RateLimiterStatus | RateLimitExceededError | null>\n\nexport type RateLimiterStatus = {\n limit: number\n duration: number\n remainingPoints: number\n msBeforeNext: number\n consumedPoints: number\n isFirstInDuration: boolean\n}\n\nexport type RateLimiterResetOptions<\n C extends RateLimiterContext = RateLimiterContext,\n> = {\n calcKey?: CalcKeyFn<C>\n}\n\nexport type RateLimiterReset<\n C extends RateLimiterContext = RateLimiterContext,\n> = (ctx: C, opts?: RateLimiterResetOptions<C>) => Promise<void>\n\nexport type RateLimiterOptions<\n C extends RateLimiterContext = RateLimiterContext,\n> = {\n keyPrefix: string\n durationMs: number\n points: number\n calcKey: CalcKeyFn<C>\n calcPoints: CalcPointsFn<C>\n failClosed?: boolean\n}\n\nexport class RateLimiter<C extends RateLimiterContext = RateLimiterContext>\n implements RateLimiterI<C>\n{\n private readonly failClosed?: boolean\n private readonly calcKey: CalcKeyFn<C>\n private readonly calcPoints: CalcPointsFn<C>\n\n constructor(\n public limiter: RateLimiterAbstract,\n options: RateLimiterOptions<C>,\n ) {\n this.limiter = limiter\n this.failClosed = options.failClosed ?? false\n this.calcKey = options.calcKey\n this.calcPoints = options.calcPoints\n }\n\n async consume(\n ctx: C,\n opts?: RateLimiterConsumeOptions<C>,\n ): Promise<RateLimiterStatus | RateLimitExceededError | null> {\n const calcKey = opts?.calcKey ?? this.calcKey\n const key = calcKey(ctx)\n if (key === null) {\n return null\n }\n const calcPoints = opts?.calcPoints ?? this.calcPoints\n const points = calcPoints(ctx)\n if (points < 1) {\n return null\n }\n try {\n const res = await this.limiter.consume(key, points)\n return formatLimiterStatus(this.limiter, res)\n } catch (err) {\n // yes this library rejects with a res not an error\n if (err instanceof RateLimiterRes) {\n const status = formatLimiterStatus(this.limiter, err)\n return new RateLimitExceededError(status)\n } else {\n if (this.failClosed) {\n throw err\n }\n logger.error(\n {\n err,\n keyPrefix: this.limiter.keyPrefix,\n points: this.limiter.points,\n duration: this.limiter.duration,\n },\n 'rate limiter failed to consume points',\n )\n return null\n }\n }\n }\n\n async reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void> {\n const key = opts?.calcKey ? opts.calcKey(ctx) : this.calcKey(ctx)\n if (key === null) {\n return\n }\n\n try {\n await this.limiter.delete(key)\n } catch (cause) {\n throw new Error(`rate limiter failed to reset key: ${key}`, { cause })\n }\n }\n}\n\nexport class MemoryRateLimiter<\n C extends RateLimiterContext = RateLimiterContext,\n> extends RateLimiter<C> {\n constructor(options: RateLimiterOptions<C>) {\n const limiter = new RateLimiterMemory({\n keyPrefix: options.keyPrefix,\n duration: Math.floor(options.durationMs / 1000),\n points: options.points,\n })\n super(limiter, options)\n }\n}\n\nexport class RedisRateLimiter<\n C extends RateLimiterContext = RateLimiterContext,\n> extends RateLimiter<C> {\n constructor(storeClient: unknown, options: RateLimiterOptions<C>) {\n const limiter = new RateLimiterRedis({\n storeClient,\n keyPrefix: options.keyPrefix,\n duration: Math.floor(options.durationMs / 1000),\n points: options.points,\n })\n super(limiter, options)\n }\n}\n\nexport const formatLimiterStatus = (\n limiter: RateLimiterAbstract,\n res: RateLimiterRes,\n): RateLimiterStatus => {\n return {\n limit: limiter.points,\n duration: limiter.duration,\n remainingPoints: res.remainingPoints,\n msBeforeNext: res.msBeforeNext,\n consumedPoints: res.consumedPoints,\n isFirstInDuration: res.isFirstInDuration,\n }\n}\n\nexport type WrappedRateLimiterOptions<\n C extends RateLimiterContext = RateLimiterContext,\n> = {\n calcKey?: CalcKeyFn<C>\n calcPoints?: CalcPointsFn<C>\n}\n\n/**\n * Wraps a {@link RateLimiterI} instance with custom key and points calculation\n * functions.\n */\nexport class WrappedRateLimiter<\n C extends RateLimiterContext = RateLimiterContext,\n> implements RateLimiterI<C>\n{\n private constructor(\n private readonly rateLimiter: RateLimiterI<C>,\n private readonly options: Readonly<WrappedRateLimiterOptions<C>>,\n ) {}\n\n async consume(ctx: C, opts?: RateLimiterConsumeOptions<C>) {\n return this.rateLimiter.consume(ctx, {\n calcKey: opts?.calcKey ?? this.options.calcKey,\n calcPoints: opts?.calcPoints ?? this.options.calcPoints,\n })\n }\n\n async reset(ctx: C, opts?: RateLimiterResetOptions<C>) {\n return this.rateLimiter.reset(ctx, {\n calcKey: opts?.calcKey ?? this.options.calcKey,\n })\n }\n\n static from<C extends RateLimiterContext = RateLimiterContext>(\n rateLimiter: RateLimiterI<C>,\n { calcKey, calcPoints }: WrappedRateLimiterOptions<C> = {},\n ): RateLimiterI<C> {\n if (!calcKey && !calcPoints) return rateLimiter\n return new WrappedRateLimiter<C>(rateLimiter, { calcKey, calcPoints })\n }\n}\n\n/**\n * Combines multiple rate limiters into one.\n *\n * The combined rate limiter will return the tightest (most restrictive) of all\n * the provided rate limiters.\n */\nexport class CombinedRateLimiter<\n C extends RateLimiterContext = RateLimiterContext,\n> implements RateLimiterI<C>\n{\n private constructor(\n private readonly rateLimiters: readonly RateLimiterI<C>[],\n ) {}\n\n async consume(ctx: C, opts?: RateLimiterConsumeOptions<C>) {\n const promises: ReturnType<RateLimiterConsume>[] = []\n for (const rl of this.rateLimiters) promises.push(rl.consume(ctx, opts))\n return Promise.all(promises).then(getTightestLimit)\n }\n\n async reset(ctx: C, opts?: RateLimiterResetOptions<C>) {\n const promises: ReturnType<RateLimiterReset>[] = []\n for (const rl of this.rateLimiters) promises.push(rl.reset(ctx, opts))\n await Promise.all(promises)\n }\n\n static from<C extends RateLimiterContext = RateLimiterContext>(\n rateLimiters: readonly RateLimiterI<C>[],\n ): RateLimiterI<C> | undefined {\n if (rateLimiters.length === 0) return undefined\n if (rateLimiters.length === 1) return rateLimiters[0]\n return new CombinedRateLimiter(rateLimiters)\n }\n}\n\nconst getTightestLimit = (\n resps: (RateLimiterStatus | RateLimitExceededError | null)[],\n): RateLimiterStatus | RateLimitExceededError | null => {\n let lowest: RateLimiterStatus | null = null\n for (const resp of resps) {\n if (resp === null) continue\n if (resp instanceof RateLimitExceededError) return resp\n if (lowest === null || resp.remainingPoints < lowest.remainingPoints) {\n lowest = resp\n }\n }\n return lowest\n}\n\nexport type RouteRateLimiterOptions<\n C extends RateLimiterContext = RateLimiterContext,\n> = {\n bypass?: (ctx: C) => boolean\n}\n\n/**\n * Wraps a {@link RateLimiterI} interface into a class that will apply the\n * appropriate headers to the response if a limit is exceeded.\n */\nexport class RouteRateLimiter<C extends RateLimiterContext = RateLimiterContext>\n implements RateLimiterI<C>\n{\n constructor(\n private readonly rateLimiter: RateLimiterI<C>,\n private readonly options: Readonly<RouteRateLimiterOptions<C>> = {},\n ) {}\n\n async handle(ctx: C): Promise<RateLimiterStatus | null> {\n const { bypass } = this.options\n if (bypass && bypass(ctx)) {\n return null\n }\n\n const result = await this.consume(ctx)\n if (result instanceof RateLimitExceededError) {\n setStatusHeaders(ctx, result.status)\n throw result\n } else if (result != null) {\n setStatusHeaders(ctx, result)\n }\n\n return result\n }\n\n async consume(...args: Parameters<RateLimiterConsume<C>>) {\n return this.rateLimiter.consume(...args)\n }\n\n async reset(...args: Parameters<RateLimiterReset<C>>) {\n return this.rateLimiter.reset(...args)\n }\n\n static from<C extends RateLimiterContext = RateLimiterContext>(\n rateLimiters: readonly RateLimiterI<C>[],\n { bypass }: RouteRateLimiterOptions<C> = {},\n ): RouteRateLimiter<C> | undefined {\n const rateLimiter = CombinedRateLimiter.from(rateLimiters)\n if (!rateLimiter) return undefined\n\n return new RouteRateLimiter(rateLimiter, { bypass })\n }\n}\n\nfunction setStatusHeaders<C extends RateLimiterContext = RateLimiterContext>(\n ctx: C,\n status: RateLimiterStatus,\n) {\n const resetAt = Math.floor((Date.now() + status.msBeforeNext) / 1e3)\n\n ctx.res?.setHeader('RateLimit-Limit', status.limit)\n ctx.res?.setHeader('RateLimit-Reset', resetAt)\n ctx.res?.setHeader('RateLimit-Remaining', status.remainingPoints)\n ctx.res?.setHeader('RateLimit-Policy', `${status.limit};w=${status.duration}`)\n}\n\nexport class RateLimitExceededError extends XRPCError {\n constructor(\n public status: RateLimiterStatus,\n errorMessage?: string,\n customErrorName?: string,\n options?: ErrorOptions,\n ) {\n super(\n ResponseType.RateLimitExceeded,\n errorMessage,\n customErrorName,\n options,\n )\n }\n\n [Symbol.hasInstance](instance: unknown): boolean {\n return (\n instance instanceof XRPCError &&\n instance.type === ResponseType.RateLimitExceeded\n )\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAmErD,MAAM,OAAO,WAAW;IAKtB,YACS,OAA4B,EACnC,OAA8B;QADvB,YAAO,GAAP,OAAO,CAAqB;QAGnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;QAC9B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;QAC9B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA;IACtC,CAAC;IAED,KAAK,CAAC,OAAO,CACX,GAAM,EACN,IAAmC;QAEnC,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAA;QAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QACxB,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,IAAI,CAAC,UAAU,CAAA;QACtD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YAC9C,OAAO,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;gBAClC,2DAA2D;gBAC3D,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;gBAChD,MAAM,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAA;YAC1C,CAAC;iBAAM,IAAI,GAAG,YAAY,sBAAsB,EAAE,CAAC;gBACjD,0CAA0C;gBAC1C,MAAM,GAAG,CAAA;YACX,CAAC;iBAAM,CAAC;gBACN,sEAAsE;gBACtE,mEAAmE;gBACnE,uDAAuD;gBACvD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;gBACxB,IAAI,OAAO;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;gBAE/D,MAAM,GAAG,CAAA;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAM,EAAE,IAAiC;QACnD,MAAM,GAAG,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACjE,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,OAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QACxE,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,iBAA+B,SAAQ,WAAc;IAChE,YAAY,OAA8B;QACxC,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC;YACpC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAA;QACF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACzB,CAAC;CACF;AAED,MAAM,OAAO,gBAA8B,SAAQ,WAAc;IAC/D,YAAY,WAAoB,EAAE,OAA8B;QAC9D,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;YACnC,WAAW;YACX,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAA;QACF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACzB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAA4B,EAC5B,GAAmB,EACA,EAAE;IACrB,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,MAAM;QACrB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;KACzC,CAAA;AACH,CAAC,CAAA;AAOD;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IAC7B,YACmB,WAA4B,EAC5B,OAA+C;QAD/C,gBAAW,GAAX,WAAW,CAAiB;QAC5B,YAAO,GAAP,OAAO,CAAwC;IAC/D,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAM,EAAE,IAAmC;QACvD,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE;YACnC,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;YAC9C,UAAU,EAAE,IAAI,EAAE,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU;SACxD,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAM,EAAE,IAAiC;QACnD,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE;YACjC,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;SAC/C,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,IAAI,CACT,WAA4B,EAC5B,EAAE,OAAO,EAAE,UAAU,KAAmC,EAAE;QAE1D,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU;YAAE,OAAO,WAAW,CAAA;QAC/C,OAAO,IAAI,kBAAkB,CAAI,WAAW,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAA;IACxE,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,mBAAmB;IAC9B,YACmB,YAAwC;QAAxC,iBAAY,GAAZ,YAAY,CAA4B;IACxD,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAM,EAAE,IAAmC;QACvD,MAAM,QAAQ,GAAqC,EAAE,CAAA;QACrD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,YAAY;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;QACxE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAE3C,mFAAmF;QACnF,IAAI,MAAM,GAA6B,IAAI,CAAA;QAC3C,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,IAAI,KAAK,IAAI;gBAAE,SAAQ;YAC3B,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;gBACrE,MAAM,GAAG,IAAI,CAAA;YACf,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAM,EAAE,IAAiC;QACnD,MAAM,QAAQ,GAAmC,EAAE,CAAA;QACnD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,YAAY;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;QACtE,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;IAED,MAAM,CAAC,IAAI,CACT,YAAwC;QAExC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAC/C,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,YAAY,CAAC,CAAC,CAAC,CAAA;QACrD,OAAO,IAAI,mBAAmB,CAAC,YAAY,CAAC,CAAA;IAC9C,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IACnD,YACS,MAAyB,EAChC,YAAqB,EACrB,eAAwB,EACxB,OAAsB;QAEtB,KAAK,CACH,YAAY,CAAC,iBAAiB,EAC9B,YAAY,EACZ,eAAe,EACf,OAAO,CACR,CAAA;QAVM,WAAM,GAAN,MAAM,CAAmB;IAWlC,CAAC;IAED,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAiB;QACpC,OAAO,CACL,QAAQ,YAAY,SAAS;YAC7B,QAAQ,CAAC,IAAI,KAAK,YAAY,CAAC,iBAAiB,CACjD,CAAA;IACH,CAAC;CACF","sourcesContent":["import {\n RateLimiterAbstract,\n RateLimiterMemory,\n RateLimiterRedis,\n RateLimiterRes,\n} from 'rate-limiter-flexible'\nimport { ResponseType, XRPCError } from './errors.js'\n\n// @NOTE Do not depend (directly or indirectly) on \"./types\" here, as it would\n// create a circular dependency.\n\nexport type { RateLimiterAbstract }\n\nexport type CalcKeyFn<C = unknown> = (ctx: C) => string | null\nexport type CalcPointsFn<C = unknown> = (ctx: C) => number\n\nexport interface RateLimiterI<C = unknown> {\n consume: RateLimiterConsume<C>\n reset: RateLimiterReset<C>\n}\n\nexport type RateLimiterConsumeOptions<C = unknown> = {\n calcKey?: CalcKeyFn<C>\n calcPoints?: CalcPointsFn<C>\n}\n\nexport type RateLimiterConsume<C = unknown> = (\n ctx: C,\n opts?: RateLimiterConsumeOptions<C>,\n) => Promise<RateLimiterStatus | null>\n\nexport type RateLimiterStatus = {\n limit: number\n duration: number\n remainingPoints: number\n msBeforeNext: number\n consumedPoints: number\n isFirstInDuration: boolean\n}\n\nexport type RateLimiterResetOptions<C = unknown> = {\n calcKey?: CalcKeyFn<C>\n}\n\nexport type RateLimiterReset<C = unknown> = (\n ctx: C,\n opts?: RateLimiterResetOptions<C>,\n) => Promise<void>\n\nexport type RateLimiterErrorHandlerDetails = {\n key: string\n points: number\n limiter: {\n keyPrefix: string\n points: number\n duration: number\n }\n}\nexport type RateLimiterErrorHandler<C = unknown> = (\n err: unknown,\n ctx: C,\n details: RateLimiterErrorHandlerDetails,\n) => Promise<RateLimiterStatus | null>\n\nexport type RateLimiterOptions<C = unknown> = {\n keyPrefix: string\n durationMs: number\n points: number\n calcKey: CalcKeyFn<C>\n calcPoints: CalcPointsFn<C>\n onError?: RateLimiterErrorHandler<C>\n}\n\nexport class RateLimiter<C = unknown> implements RateLimiterI<C> {\n private readonly onError?: RateLimiterErrorHandler<C>\n private readonly calcKey: CalcKeyFn<C>\n private readonly calcPoints: CalcPointsFn<C>\n\n constructor(\n public limiter: RateLimiterAbstract,\n options: RateLimiterOptions<C>,\n ) {\n this.limiter = limiter\n this.onError = options.onError\n this.calcKey = options.calcKey\n this.calcPoints = options.calcPoints\n }\n\n async consume(\n ctx: C,\n opts?: RateLimiterConsumeOptions<C>,\n ): Promise<RateLimiterStatus | null> {\n const calcKey = opts?.calcKey ?? this.calcKey\n const key = calcKey(ctx)\n if (key === null) {\n return null\n }\n const calcPoints = opts?.calcPoints ?? this.calcPoints\n const points = calcPoints(ctx)\n if (points < 1) {\n return null\n }\n const { limiter } = this\n try {\n const res = await limiter.consume(key, points)\n return formatLimiterStatus(limiter, res)\n } catch (err) {\n if (err instanceof RateLimiterRes) {\n // Wrap rate-limiter-flexible error into our own error type\n const status = formatLimiterStatus(limiter, err)\n throw new RateLimitExceededError(status)\n } else if (err instanceof RateLimitExceededError) {\n // Propagate RateLimitExceededError errors\n throw err\n } else {\n // Most likely a system error (failed to connect to Redis, etc). Allow\n // the caller to decide how to handle it (fail open by allowing the\n // request, fail closed by rejecting the request, etc).\n const { onError } = this\n if (onError) return onError(err, ctx, { key, points, limiter })\n\n throw err\n }\n }\n }\n\n async reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void> {\n const key = opts?.calcKey ? opts.calcKey(ctx) : this.calcKey(ctx)\n if (key === null) {\n return\n }\n\n try {\n await this.limiter.delete(key)\n } catch (cause) {\n throw new Error(`rate limiter failed to reset key: ${key}`, { cause })\n }\n }\n}\n\nexport class MemoryRateLimiter<C = unknown> extends RateLimiter<C> {\n constructor(options: RateLimiterOptions<C>) {\n const limiter = new RateLimiterMemory({\n keyPrefix: options.keyPrefix,\n duration: Math.floor(options.durationMs / 1000),\n points: options.points,\n })\n super(limiter, options)\n }\n}\n\nexport class RedisRateLimiter<C = unknown> extends RateLimiter<C> {\n constructor(storeClient: unknown, options: RateLimiterOptions<C>) {\n const limiter = new RateLimiterRedis({\n storeClient,\n keyPrefix: options.keyPrefix,\n duration: Math.floor(options.durationMs / 1000),\n points: options.points,\n })\n super(limiter, options)\n }\n}\n\nexport const formatLimiterStatus = (\n limiter: RateLimiterAbstract,\n res: RateLimiterRes,\n): RateLimiterStatus => {\n return {\n limit: limiter.points,\n duration: limiter.duration,\n remainingPoints: res.remainingPoints,\n msBeforeNext: res.msBeforeNext,\n consumedPoints: res.consumedPoints,\n isFirstInDuration: res.isFirstInDuration,\n }\n}\n\nexport type WrappedRateLimiterOptions<C = unknown> = {\n calcKey?: CalcKeyFn<C>\n calcPoints?: CalcPointsFn<C>\n}\n\n/**\n * Wraps a {@link RateLimiterI} instance with custom key and points calculation\n * functions.\n */\nexport class WrappedRateLimiter<C = unknown> implements RateLimiterI<C> {\n private constructor(\n private readonly rateLimiter: RateLimiterI<C>,\n private readonly options: Readonly<WrappedRateLimiterOptions<C>>,\n ) {}\n\n async consume(ctx: C, opts?: RateLimiterConsumeOptions<C>) {\n return this.rateLimiter.consume(ctx, {\n calcKey: opts?.calcKey ?? this.options.calcKey,\n calcPoints: opts?.calcPoints ?? this.options.calcPoints,\n })\n }\n\n async reset(ctx: C, opts?: RateLimiterResetOptions<C>) {\n return this.rateLimiter.reset(ctx, {\n calcKey: opts?.calcKey ?? this.options.calcKey,\n })\n }\n\n static from<C = unknown>(\n rateLimiter: RateLimiterI<C>,\n { calcKey, calcPoints }: WrappedRateLimiterOptions<C> = {},\n ): RateLimiterI<C> {\n if (!calcKey && !calcPoints) return rateLimiter\n return new WrappedRateLimiter<C>(rateLimiter, { calcKey, calcPoints })\n }\n}\n\n/**\n * Combines multiple rate limiters into one.\n *\n * The combined rate limiter will return the tightest (most restrictive) of all\n * the provided rate limiters.\n */\nexport class CombinedRateLimiter<C = unknown> implements RateLimiterI<C> {\n private constructor(\n private readonly rateLimiters: readonly RateLimiterI<C>[],\n ) {}\n\n async consume(ctx: C, opts?: RateLimiterConsumeOptions<C>) {\n const promises: ReturnType<RateLimiterConsume>[] = []\n for (const rl of this.rateLimiters) promises.push(rl.consume(ctx, opts))\n const results = await Promise.all(promises)\n\n // Compute the tightest rate limit status (the one with the least remaining points)\n let lowest: RateLimiterStatus | null = null\n for (const resp of results) {\n if (resp === null) continue\n if (lowest === null || resp.remainingPoints < lowest.remainingPoints) {\n lowest = resp\n }\n }\n\n return lowest\n }\n\n async reset(ctx: C, opts?: RateLimiterResetOptions<C>) {\n const promises: ReturnType<RateLimiterReset>[] = []\n for (const rl of this.rateLimiters) promises.push(rl.reset(ctx, opts))\n await Promise.all(promises)\n }\n\n static from<C = unknown>(\n rateLimiters: readonly RateLimiterI<C>[],\n ): RateLimiterI<C> | undefined {\n if (rateLimiters.length === 0) return undefined\n if (rateLimiters.length === 1) return rateLimiters[0]\n return new CombinedRateLimiter(rateLimiters)\n }\n}\n\nexport class RateLimitExceededError extends XRPCError {\n constructor(\n public status: RateLimiterStatus,\n errorMessage?: string,\n customErrorName?: string,\n options?: ErrorOptions,\n ) {\n super(\n ResponseType.RateLimitExceeded,\n errorMessage,\n customErrorName,\n options,\n )\n }\n\n [Symbol.hasInstance](instance: unknown): boolean {\n return (\n instance instanceof XRPCError &&\n instance.type === ResponseType.RateLimitExceeded\n )\n }\n}\n"]}
|
package/dist/server.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { IncomingMessage } from 'node:http';
|
|
|
2
2
|
import { Express, RequestHandler, Router } from 'express';
|
|
3
3
|
import { l } from '@atproto/lex-schema';
|
|
4
4
|
import { LexXrpcProcedure, LexXrpcQuery, LexXrpcSubscription, LexiconDoc, Lexicons } from '@atproto/lexicon';
|
|
5
|
-
import {
|
|
5
|
+
import { HttpRateLimiter } from './rate-limiter-http.js';
|
|
6
|
+
import { RateLimiterI } from './rate-limiter.js';
|
|
6
7
|
import { XrpcStreamServer } from './stream/index.js';
|
|
7
8
|
import { Auth, AuthResult, CatchallHandler, HandlerContext, Input, LexMethodConfig, LexMethodHandler, LexSubscriptionConfig, LexSubscriptionHandler, MethodAuthContext, MethodConfig, MethodConfigOrHandler, MethodHandler, Options, Output, Params, StreamAuthContext, StreamConfig, StreamConfigOrHandler, StreamContext } from './types.js';
|
|
8
9
|
import { AuthVerifierInternal, InputVerifierInternal, OutputVerifierInternal, ParamsVerifierInternal } from './util.js';
|
|
@@ -13,7 +14,7 @@ export declare class Server {
|
|
|
13
14
|
subscriptions: Map<string, XrpcStreamServer>;
|
|
14
15
|
lex: Lexicons;
|
|
15
16
|
options: Options;
|
|
16
|
-
globalRateLimiter?:
|
|
17
|
+
globalRateLimiter?: HttpRateLimiter<HandlerContext>;
|
|
17
18
|
sharedRateLimiters?: Map<string, RateLimiterI<HandlerContext>>;
|
|
18
19
|
constructor(lexicons?: LexiconDoc[], opts?: Options);
|
|
19
20
|
listen(port: number, callback?: () => void): import("http").Server<typeof IncomingMessage, typeof import("http").ServerResponse>;
|
|
@@ -35,7 +36,7 @@ export declare class Server {
|
|
|
35
36
|
protected addRoute<A extends Auth = Auth>(nsid: string, def: LexXrpcQuery | LexXrpcProcedure, config: MethodConfig<A>): Promise<void>;
|
|
36
37
|
catchall: CatchallHandler;
|
|
37
38
|
createHandler<A extends Auth = Auth, P extends Params = Params, I extends Input = Input, O extends Output = Output>(nsid: string, def: LexXrpcQuery | LexXrpcProcedure, cfg: MethodConfig<A, P, I, O>): RequestHandler;
|
|
38
|
-
protected createHandlerInternal<A extends Auth, P extends Params, I extends Input, O extends Output>(authVerifier: AuthVerifierInternal<MethodAuthContext<P>, A> | null, paramsVerifier: ParamsVerifierInternal<P>, inputVerifier: InputVerifierInternal<I>, routeLimiter:
|
|
39
|
+
protected createHandlerInternal<A extends Auth, P extends Params, I extends Input, O extends Output>(authVerifier: AuthVerifierInternal<MethodAuthContext<P>, A> | null, paramsVerifier: ParamsVerifierInternal<P>, inputVerifier: InputVerifierInternal<I>, routeLimiter: HttpRateLimiter<HandlerContext<A, P, I>> | undefined, handler: MethodHandler<A, P, I, O>, validateResOutput: null | OutputVerifierInternal<O>): RequestHandler;
|
|
39
40
|
protected addSubscription<A extends Auth = Auth>(nsid: string, def: LexXrpcSubscription, cfg: StreamConfig<A, Params>): Promise<void>;
|
|
40
41
|
protected addSubscriptionInternal<A extends Auth, P extends Params>(nsid: string, paramsVerifier: ParamsVerifierInternal<P>, authVerifier: AuthVerifierInternal<StreamAuthContext<P>, A> | null, handler: (ctx: StreamContext<A, P>) => AsyncIterable<unknown>): void;
|
|
41
42
|
private createAuthVerifier;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAG3C,OAAgB,EAGd,OAAO,EACP,cAAc,EACd,MAAM,EACP,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AACvC,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,QAAQ,EAET,MAAM,kBAAkB,CAAA;AASzB,OAAO,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|