@atproto/xrpc-server 0.11.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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # @atproto/xrpc-server
2
2
 
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
+
3
12
  ## 0.11.0
4
13
 
5
14
  ### Minor Changes
@@ -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"]}
@@ -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 interface RateLimiterContext {
5
- req: IncomingMessage;
6
- res?: ServerResponse;
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 extends RateLimiterContext = RateLimiterContext> = {
10
+ export type RateLimiterConsumeOptions<C = unknown> = {
15
11
  calcKey?: CalcKeyFn<C>;
16
12
  calcPoints?: CalcPointsFn<C>;
17
13
  };
18
- export type RateLimiterConsume<C extends RateLimiterContext = RateLimiterContext> = (ctx: C, opts?: RateLimiterConsumeOptions<C>) => Promise<RateLimiterStatus | RateLimitExceededError | null>;
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 extends RateLimiterContext = RateLimiterContext> = {
23
+ export type RateLimiterResetOptions<C = unknown> = {
28
24
  calcKey?: CalcKeyFn<C>;
29
25
  };
30
- export type RateLimiterReset<C extends RateLimiterContext = RateLimiterContext> = (ctx: C, opts?: RateLimiterResetOptions<C>) => Promise<void>;
31
- export type RateLimiterOptions<C extends RateLimiterContext = RateLimiterContext> = {
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
- failClosed?: boolean;
43
+ onError?: RateLimiterErrorHandler<C>;
38
44
  };
39
- export declare class RateLimiter<C extends RateLimiterContext = RateLimiterContext> implements RateLimiterI<C> {
45
+ export declare class RateLimiter<C = unknown> implements RateLimiterI<C> {
40
46
  limiter: RateLimiterAbstract;
41
- private readonly failClosed?;
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 | RateLimitExceededError | null>;
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 extends RateLimiterContext = RateLimiterContext> extends RateLimiter<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 extends RateLimiterContext = RateLimiterContext> extends RateLimiter<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 extends RateLimiterContext = RateLimiterContext> = {
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 extends RateLimiterContext = RateLimiterContext> implements RateLimiterI<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 | RateLimitExceededError | null>;
73
+ consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus | null>;
68
74
  reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void>;
69
- static from<C extends RateLimiterContext = RateLimiterContext>(rateLimiter: RateLimiterI<C>, { calcKey, calcPoints }?: WrappedRateLimiterOptions<C>): RateLimiterI<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 extends RateLimiterContext = RateLimiterContext> implements RateLimiterI<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 | RateLimitExceededError | null>;
86
+ consume(ctx: C, opts?: RateLimiterConsumeOptions<C>): Promise<RateLimiterStatus | null>;
81
87
  reset(ctx: C, opts?: RateLimiterResetOptions<C>): Promise<void>;
82
- static from<C extends RateLimiterContext = RateLimiterContext>(rateLimiters: readonly RateLimiterI<C>[]): RateLimiterI<C> | undefined;
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":"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"}
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -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.failClosed = options.failClosed ?? false;
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 this.limiter.consume(key, points);
25
- return formatLimiterStatus(this.limiter, res);
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
- const status = formatLimiterStatus(this.limiter, err);
31
- return new RateLimitExceededError(status);
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
- if (this.failClosed) {
35
- throw err;
36
- }
37
- logger.error({
38
- err,
39
- keyPrefix: this.limiter.keyPrefix,
40
- points: this.limiter.points,
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
- return Promise.all(promises).then(getTightestLimit);
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);
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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","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":"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","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 { RateLimiterI, RouteRateLimiter } from './rate-limiter.js';
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?: RouteRateLimiter<HandlerContext>;
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: RouteRateLimiter<HandlerContext<A, P, I>> | undefined, handler: MethodHandler<A, P, I, O>, validateResOutput: null | OutputVerifierInternal<O>): RequestHandler;
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;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -6,7 +6,8 @@ import { l } from '@atproto/lex-schema';
6
6
  import { Lexicons, lexToJson, } from '@atproto/lexicon';
7
7
  import { InternalServerError, InvalidRequestError, MethodNotImplementedError, XRPCError, excludeErrorResult, } from './errors.js';
8
8
  import log, { LOGGER_NAME } from './logger.js';
9
- import { RouteRateLimiter, WrappedRateLimiter, } from './rate-limiter.js';
9
+ import { HttpRateLimiter } from './rate-limiter-http.js';
10
+ import { WrappedRateLimiter, } from './rate-limiter.js';
10
11
  import { ErrorFrame, Frame, MessageFrame, XrpcStreamServer, } from './stream/index.js';
11
12
  import { isHandlerPipeThroughBuffer, isHandlerPipeThroughStream, isHandlerSuccess, isSharedRateLimitOpts, } from './types.js';
12
13
  import { asArray, createLexiconInputVerifier, createLexiconOutputVerifier, createLexiconParamsVerifier, createSchemaInputVerifier, createSchemaOutputVerifier, createSchemaParamsVerifier, extractUrlNsid, setHeaders, } from './util.js';
@@ -75,7 +76,7 @@ export class Server {
75
76
  if (opts.rateLimits) {
76
77
  const { global, shared, creator, bypass } = opts.rateLimits;
77
78
  if (global) {
78
- this.globalRateLimiter = RouteRateLimiter.from(global.map((options) => creator(buildRateLimiterOptions(options))), { bypass });
79
+ this.globalRateLimiter = HttpRateLimiter.from(global.map((options) => creator(buildRateLimiterOptions(options))), { bypass });
79
80
  }
80
81
  if (shared) {
81
82
  this.sharedRateLimiters = new Map(shared.map((options) => [
@@ -356,8 +357,9 @@ export class Server {
356
357
  createRouteRateLimiter(nsid, config) {
357
358
  // @NOTE global & shared rate limiters are instantiated with a context of
358
359
  // HandlerContext which is compatible (more generic) with the context of
359
- // this route specific rate limiters (C). For this reason, it's safe to
360
- // cast these with an `any` context
360
+ // this route specific rate limiters (C). For this reason, it's safe to cast
361
+ // the context of the global rate limiter to the context of the route
362
+ // specific rate limiter (HandlerContext<A, P, I>).
361
363
  const globalRateLimiter = this.globalRateLimiter;
362
364
  // No route specific rate limiting configured, use the global rate limiter.
363
365
  if (!config.rateLimit)
@@ -392,7 +394,9 @@ export class Server {
392
394
  // the route specific rate limiters.
393
395
  if (globalRateLimiter)
394
396
  rateLimiters.push(globalRateLimiter);
395
- return RouteRateLimiter.from(rateLimiters, { bypass });
397
+ return HttpRateLimiter.from(rateLimiters, {
398
+ bypass,
399
+ });
396
400
  }
397
401
  }
398
402
  function createErrorMiddleware({ errorParser = (err) => XRPCError.fromError(err), }) {
@@ -447,8 +451,17 @@ function toSimplifiedErrorLike(err) {
447
451
  }
448
452
  return err;
449
453
  }
450
- function buildRateLimiterOptions({ name, calcKey = defaultKey, calcPoints = defaultPoints, ...desc }) {
451
- return { ...desc, calcKey, calcPoints, keyPrefix: `rl-${name}` };
454
+ function buildRateLimiterOptions({ name, calcKey = defaultKey, calcPoints = defaultPoints, failClosed = false, durationMs, points, }) {
455
+ return {
456
+ durationMs,
457
+ points,
458
+ calcKey,
459
+ calcPoints,
460
+ keyPrefix: `rl-${name}`,
461
+ onError: failClosed
462
+ ? undefined // Let the error propagate
463
+ : rateLimiterLoggerErrorHandler,
464
+ };
452
465
  }
453
466
  const defaultPoints = () => 1;
454
467
  /**
@@ -458,4 +471,10 @@ const defaultPoints = () => 1;
458
471
  * @see {@link https://expressjs.com/en/guide/behind-proxies.html}
459
472
  */
460
473
  const defaultKey = ({ req }) => req.ip;
474
+ async function rateLimiterLoggerErrorHandler(err, ctx, { limiter: { keyPrefix, points, duration } }) {
475
+ const { req } = ctx;
476
+ const logger = isPinoHttpRequest(req) ? req.log : log;
477
+ logger.error({ err, keyPrefix, points, duration }, 'rate limiter failed to consume points');
478
+ return null;
479
+ }
461
480
  //# sourceMappingURL=server.js.map