@xyph3r/rate-limiter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/dist/adapters/express.d.ts +26 -0
- package/dist/adapters/express.d.ts.map +1 -0
- package/dist/adapters/express.js +51 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/fastify.d.ts +24 -0
- package/dist/adapters/fastify.d.ts.map +1 -0
- package/dist/adapters/fastify.js +40 -0
- package/dist/adapters/fastify.js.map +1 -0
- package/dist/adapters/fetch.d.ts +17 -0
- package/dist/adapters/fetch.d.ts.map +1 -0
- package/dist/adapters/fetch.js +53 -0
- package/dist/adapters/fetch.js.map +1 -0
- package/dist/adapters/hono.d.ts +25 -0
- package/dist/adapters/hono.d.ts.map +1 -0
- package/dist/adapters/hono.js +40 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/nest.d.ts +32 -0
- package/dist/adapters/nest.d.ts.map +1 -0
- package/dist/adapters/nest.js +47 -0
- package/dist/adapters/nest.js.map +1 -0
- package/dist/adapters/next.d.ts +12 -0
- package/dist/adapters/next.d.ts.map +1 -0
- package/dist/adapters/next.js +11 -0
- package/dist/adapters/next.js.map +1 -0
- package/dist/core/cached-rate-limiter-proxy.d.ts +17 -0
- package/dist/core/cached-rate-limiter-proxy.d.ts.map +1 -0
- package/dist/core/cached-rate-limiter-proxy.js +47 -0
- package/dist/core/cached-rate-limiter-proxy.js.map +1 -0
- package/dist/core/create-rate-limiter.d.ts +11 -0
- package/dist/core/create-rate-limiter.d.ts.map +1 -0
- package/dist/core/create-rate-limiter.js +34 -0
- package/dist/core/create-rate-limiter.js.map +1 -0
- package/dist/core/rate-limiter-builder.d.ts +27 -0
- package/dist/core/rate-limiter-builder.d.ts.map +1 -0
- package/dist/core/rate-limiter-builder.js +73 -0
- package/dist/core/rate-limiter-builder.js.map +1 -0
- package/dist/core/rate-limiter.d.ts +23 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js +59 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/strategy-factory.d.ts +18 -0
- package/dist/core/strategy-factory.d.ts.map +1 -0
- package/dist/core/strategy-factory.js +17 -0
- package/dist/core/strategy-factory.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/stores/memory-store.d.ts +9 -0
- package/dist/stores/memory-store.d.ts.map +1 -0
- package/dist/stores/memory-store.js +24 -0
- package/dist/stores/memory-store.js.map +1 -0
- package/dist/stores/rate-limit-store.d.ts +6 -0
- package/dist/stores/rate-limit-store.d.ts.map +1 -0
- package/dist/stores/rate-limit-store.js +2 -0
- package/dist/stores/rate-limit-store.js.map +1 -0
- package/dist/stores/redis-store.d.ts +26 -0
- package/dist/stores/redis-store.d.ts.map +1 -0
- package/dist/stores/redis-store.js +41 -0
- package/dist/stores/redis-store.js.map +1 -0
- package/dist/strategies/sliding-window-strategy.d.ts +31 -0
- package/dist/strategies/sliding-window-strategy.d.ts.map +1 -0
- package/dist/strategies/sliding-window-strategy.js +212 -0
- package/dist/strategies/sliding-window-strategy.js.map +1 -0
- package/dist/strategies/token-bucket-strategy.d.ts +30 -0
- package/dist/strategies/token-bucket-strategy.d.ts.map +1 -0
- package/dist/strategies/token-bucket-strategy.js +154 -0
- package/dist/strategies/token-bucket-strategy.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/headers.d.ts +4 -0
- package/dist/utils/headers.d.ts.map +1 -0
- package/dist/utils/headers.js +16 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/http.d.ts +11 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +41 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/math.d.ts +4 -0
- package/dist/utils/math.d.ts.map +1 -0
- package/dist/utils/math.js +21 -0
- package/dist/utils/math.js.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type HeaderCarrier } from "../utils/http.js";
|
|
2
|
+
import type { RateLimitDecision, RateLimiterLike } from "../types.js";
|
|
3
|
+
export interface NestLikeRequest extends HeaderCarrier {
|
|
4
|
+
}
|
|
5
|
+
export interface NestLikeResponse {
|
|
6
|
+
header?(name: string, value: string): unknown;
|
|
7
|
+
setHeader?(name: string, value: string): unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface NestHttpArgumentsHost<TRequest extends NestLikeRequest = NestLikeRequest, TResponse extends NestLikeResponse = NestLikeResponse> {
|
|
10
|
+
getRequest(): TRequest;
|
|
11
|
+
getResponse(): TResponse;
|
|
12
|
+
}
|
|
13
|
+
export interface NestExecutionContext<TRequest extends NestLikeRequest = NestLikeRequest, TResponse extends NestLikeResponse = NestLikeResponse> {
|
|
14
|
+
switchToHttp(): NestHttpArgumentsHost<TRequest, TResponse>;
|
|
15
|
+
}
|
|
16
|
+
export interface NestRateLimitOptions<TRequest extends NestLikeRequest = NestLikeRequest, TResponse extends NestLikeResponse = NestLikeResponse> {
|
|
17
|
+
cost?: (request: TRequest, response: TResponse) => number | Promise<number>;
|
|
18
|
+
errorFactory?: (decision: RateLimitDecision, request: TRequest, response: TResponse) => unknown;
|
|
19
|
+
key?: (request: TRequest, response: TResponse) => string | Promise<string>;
|
|
20
|
+
setHeaders?: boolean;
|
|
21
|
+
skip?: (request: TRequest, response: TResponse) => boolean | Promise<boolean>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Pattern: Decorator
|
|
25
|
+
* Problem: NestJS needs a guard-shaped integration point, but the limiter should stay decoupled from Nest internals.
|
|
26
|
+
* Solution: The adapter decorates the core limiter behind a minimal `canActivate()` contract.
|
|
27
|
+
* Trade-off: Users supply their own Nest exception in `errorFactory`; justified to keep Nest optional.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createNestRateLimitGuard<TRequest extends NestLikeRequest = NestLikeRequest, TResponse extends NestLikeResponse = NestLikeResponse>(limiter: RateLimiterLike, options?: NestRateLimitOptions<TRequest, TResponse>): {
|
|
30
|
+
canActivate(context: NestExecutionContext<TRequest, TResponse>): Promise<boolean>;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=nest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nest.d.ts","sourceRoot":"","sources":["../../src/adapters/nest.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,WAAW,eAAgB,SAAQ,aAAa;CAAG;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,SAAS,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAClD;AAED,MAAM,WAAW,qBAAqB,CACpC,QAAQ,SAAS,eAAe,GAAG,eAAe,EAClD,SAAS,SAAS,gBAAgB,GAAG,gBAAgB;IAErD,UAAU,IAAI,QAAQ,CAAC;IACvB,WAAW,IAAI,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB,CACnC,QAAQ,SAAS,eAAe,GAAG,eAAe,EAClD,SAAS,SAAS,gBAAgB,GAAG,gBAAgB;IAErD,YAAY,IAAI,qBAAqB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,oBAAoB,CACnC,QAAQ,SAAS,eAAe,GAAG,eAAe,EAClD,SAAS,SAAS,gBAAgB,GAAG,gBAAgB;IAErD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E,YAAY,CAAC,EAAE,CACb,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,SAAS,KAChB,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,SAAS,eAAe,GAAG,eAAe,EAClD,SAAS,SAAS,gBAAgB,GAAG,gBAAgB,EAErD,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,oBAAoB,CAAC,QAAQ,EAAE,SAAS,CAAM,GACtD;IAAE,WAAW,CAAC,OAAO,EAAE,oBAAoB,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAiCvF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { applyHeaders } from "../utils/headers.js";
|
|
2
|
+
import { getDefaultKeyFromRequest } from "../utils/http.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pattern: Decorator
|
|
5
|
+
* Problem: NestJS needs a guard-shaped integration point, but the limiter should stay decoupled from Nest internals.
|
|
6
|
+
* Solution: The adapter decorates the core limiter behind a minimal `canActivate()` contract.
|
|
7
|
+
* Trade-off: Users supply their own Nest exception in `errorFactory`; justified to keep Nest optional.
|
|
8
|
+
*/
|
|
9
|
+
export function createNestRateLimitGuard(limiter, options = {}) {
|
|
10
|
+
return {
|
|
11
|
+
async canActivate(context) {
|
|
12
|
+
const http = context.switchToHttp();
|
|
13
|
+
const request = http.getRequest();
|
|
14
|
+
const response = http.getResponse();
|
|
15
|
+
if ((await options.skip?.(request, response)) === true) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
const key = (await options.key?.(request, response)) ??
|
|
19
|
+
getDefaultKeyFromRequest(request);
|
|
20
|
+
const cost = (await options.cost?.(request, response)) ?? 1;
|
|
21
|
+
const decision = await limiter.check(key, { cost });
|
|
22
|
+
if (options.setHeaders !== false) {
|
|
23
|
+
applyHeaders((name, value) => setNestHeader(response, name, value), decision);
|
|
24
|
+
}
|
|
25
|
+
if (decision.allowed) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
throw (options.errorFactory?.(decision, request, response) ??
|
|
29
|
+
defaultNestRateLimitError(decision));
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function setNestHeader(response, name, value) {
|
|
34
|
+
if (typeof response.setHeader === "function") {
|
|
35
|
+
response.setHeader(name, value);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (typeof response.header === "function") {
|
|
39
|
+
response.header(name, value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function defaultNestRateLimitError(decision) {
|
|
43
|
+
const error = new Error("Too many requests");
|
|
44
|
+
Object.assign(error, { decision, statusCode: 429 });
|
|
45
|
+
return error;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=nest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nest.js","sourceRoot":"","sources":["../../src/adapters/nest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAsB,MAAM,kBAAkB,CAAC;AAwChF;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAItC,OAAwB,EACxB,UAAqD,EAAE;IAEvD,OAAO;QACL,KAAK,CAAC,WAAW,CACf,OAAkD;YAElD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAEpC,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACvD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,GAAG,GACP,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;gBACxC,wBAAwB,CAAC,OAAO,CAAC,CAAC;YACpC,MAAM,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAEpD,IAAI,OAAO,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;gBACjC,YAAY,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;YAChF,CAAC;YAED,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,CACJ,OAAO,CAAC,YAAY,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC;gBACnD,yBAAyB,CAAC,QAAQ,CAAC,CACpC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CACpB,QAA0B,EAC1B,IAAY,EACZ,KAAa;IAEb,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC7C,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IAED,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1C,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAAC,QAA2B;IAC5D,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC7C,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type FetchLikeHandler, type FetchRateLimitOptions } from "./fetch.js";
|
|
2
|
+
import type { RateLimiterLike } from "../types.js";
|
|
3
|
+
export type NextRouteHandler<TContext = unknown> = FetchLikeHandler<TContext>;
|
|
4
|
+
export type NextRateLimitOptions<TContext = unknown> = FetchRateLimitOptions<TContext>;
|
|
5
|
+
/**
|
|
6
|
+
* Pattern: Decorator
|
|
7
|
+
* Problem: Next.js route handlers need the same core behavior as Bun without a framework dependency in the core.
|
|
8
|
+
* Solution: The Next.js adapter is a thin decorator over the shared fetch adapter.
|
|
9
|
+
* Trade-off: A tiny alias layer; justified because it gives Next.js users a first-class API surface.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createNextRateLimit<TContext = unknown>(limiter: RateLimiterLike, options?: NextRateLimitOptions<TContext>): (handler: NextRouteHandler<TContext>) => NextRouteHandler<TContext>;
|
|
12
|
+
//# sourceMappingURL=next.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,gBAAgB,CAAC,QAAQ,GAAG,OAAO,IAAI,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AAC9E,MAAM,MAAM,oBAAoB,CAAC,QAAQ,GAAG,OAAO,IACjD,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAElC;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,OAAO,EACpD,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,oBAAoB,CAAC,QAAQ,CAAM,GAC3C,CACD,OAAO,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAChC,gBAAgB,CAAC,QAAQ,CAAC,CAE9B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createFetchRateLimit, } from "./fetch.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pattern: Decorator
|
|
4
|
+
* Problem: Next.js route handlers need the same core behavior as Bun without a framework dependency in the core.
|
|
5
|
+
* Solution: The Next.js adapter is a thin decorator over the shared fetch adapter.
|
|
6
|
+
* Trade-off: A tiny alias layer; justified because it gives Next.js users a first-class API surface.
|
|
7
|
+
*/
|
|
8
|
+
export function createNextRateLimit(limiter, options = {}) {
|
|
9
|
+
return createFetchRateLimit(limiter, options);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,GAGrB,MAAM,YAAY,CAAC;AAOpB;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAwB,EACxB,UAA0C,EAAE;IAI5C,OAAO,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RateLimitCheckOptions, RateLimitDecision, RateLimiterLike } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pattern: Proxy
|
|
4
|
+
* Problem: Extremely hot endpoints can hit the backing store repeatedly within the same micro-burst.
|
|
5
|
+
* Solution: This proxy caches recent decisions for a short TTL while preserving the limiter API.
|
|
6
|
+
* Trade-off: Very small staleness window; justified only for intentionally tiny cache durations.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CachedRateLimiterProxy implements RateLimiterLike {
|
|
9
|
+
private readonly inner;
|
|
10
|
+
private readonly ttlMs;
|
|
11
|
+
private readonly now;
|
|
12
|
+
private readonly entries;
|
|
13
|
+
constructor(inner: RateLimiterLike, ttlMs: number, now?: () => number);
|
|
14
|
+
check(key: string, options?: RateLimitCheckOptions): Promise<RateLimitDecision>;
|
|
15
|
+
reset(key: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=cached-rate-limiter-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-rate-limiter-proxy.d.ts","sourceRoot":"","sources":["../../src/core/cached-rate-limiter-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EAChB,MAAM,aAAa,CAAC;AAOrB;;;;;GAKG;AACH,qBAAa,sBAAuB,YAAW,eAAe;IAI1D,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;gBAGtC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAM,MAAiB;IAGzC,KAAK,CACT,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,iBAAiB,CAAC;IAkBvB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CASxC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern: Proxy
|
|
3
|
+
* Problem: Extremely hot endpoints can hit the backing store repeatedly within the same micro-burst.
|
|
4
|
+
* Solution: This proxy caches recent decisions for a short TTL while preserving the limiter API.
|
|
5
|
+
* Trade-off: Very small staleness window; justified only for intentionally tiny cache durations.
|
|
6
|
+
*/
|
|
7
|
+
export class CachedRateLimiterProxy {
|
|
8
|
+
inner;
|
|
9
|
+
ttlMs;
|
|
10
|
+
now;
|
|
11
|
+
entries = new Map();
|
|
12
|
+
constructor(inner, ttlMs, now = Date.now) {
|
|
13
|
+
this.inner = inner;
|
|
14
|
+
this.ttlMs = ttlMs;
|
|
15
|
+
this.now = now;
|
|
16
|
+
}
|
|
17
|
+
async check(key, options = {}) {
|
|
18
|
+
const cacheKey = `${key}:${options.cost ?? 1}`;
|
|
19
|
+
const currentTime = this.now();
|
|
20
|
+
const cached = this.entries.get(cacheKey);
|
|
21
|
+
if (cached && cached.expiresAt > currentTime) {
|
|
22
|
+
return cloneDecision(cached.decision);
|
|
23
|
+
}
|
|
24
|
+
const decision = await this.inner.check(key, options);
|
|
25
|
+
this.entries.set(cacheKey, {
|
|
26
|
+
decision: cloneDecision(decision),
|
|
27
|
+
expiresAt: currentTime + this.ttlMs,
|
|
28
|
+
});
|
|
29
|
+
return decision;
|
|
30
|
+
}
|
|
31
|
+
async reset(key) {
|
|
32
|
+
for (const cacheKey of this.entries.keys()) {
|
|
33
|
+
if (cacheKey === key || cacheKey.startsWith(`${key}:`)) {
|
|
34
|
+
this.entries.delete(cacheKey);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await this.inner.reset(key);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function cloneDecision(decision) {
|
|
41
|
+
return {
|
|
42
|
+
...decision,
|
|
43
|
+
checkedAt: new Date(decision.checkedAt.getTime()),
|
|
44
|
+
resetAt: new Date(decision.resetAt.getTime()),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=cached-rate-limiter-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-rate-limiter-proxy.js","sourceRoot":"","sources":["../../src/core/cached-rate-limiter-proxy.ts"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,MAAM,OAAO,sBAAsB;IAId;IACA;IACA;IALF,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEzD,YACmB,KAAsB,EACtB,KAAa,EACb,MAAoB,IAAI,CAAC,GAAG;QAF5B,UAAK,GAAL,KAAK,CAAiB;QACtB,UAAK,GAAL,KAAK,CAAQ;QACb,QAAG,GAAH,GAAG,CAAyB;IAC5C,CAAC;IAEJ,KAAK,CAAC,KAAK,CACT,GAAW,EACX,UAAiC,EAAE;QAEnC,MAAM,QAAQ,GAAG,GAAG,GAAG,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE1C,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,WAAW,EAAE,CAAC;YAC7C,OAAO,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE;YACzB,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;YACjC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC,KAAK;SACpC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACrB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC;gBACvD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,SAAS,aAAa,CAAC,QAA2B;IAChD,OAAO;QACL,GAAG,QAAQ;QACX,SAAS,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACjD,OAAO,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { StrategyConfig } from "./strategy-factory.js";
|
|
2
|
+
import type { RateLimitStore } from "../stores/rate-limit-store.js";
|
|
3
|
+
import type { RateLimiterLike } from "../types.js";
|
|
4
|
+
export type CreateRateLimiterConfig = StrategyConfig & {
|
|
5
|
+
cacheMs?: number;
|
|
6
|
+
keyPrefix?: string;
|
|
7
|
+
now?: () => number;
|
|
8
|
+
store?: RateLimitStore;
|
|
9
|
+
};
|
|
10
|
+
export declare function createRateLimiter(config: CreateRateLimiterConfig): RateLimiterLike;
|
|
11
|
+
//# sourceMappingURL=create-rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/core/create-rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,uBAAuB,GAAG,cAAc,GAAG;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,uBAAuB,GAC9B,eAAe,CAmCjB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RateLimiterBuilder } from "./rate-limiter-builder.js";
|
|
2
|
+
export function createRateLimiter(config) {
|
|
3
|
+
const builder = new RateLimiterBuilder();
|
|
4
|
+
if (config.store) {
|
|
5
|
+
builder.useStore(config.store);
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
builder.withMemoryStore();
|
|
9
|
+
}
|
|
10
|
+
if (config.keyPrefix) {
|
|
11
|
+
builder.withKeyPrefix(config.keyPrefix);
|
|
12
|
+
}
|
|
13
|
+
if (config.now) {
|
|
14
|
+
builder.withClock(config.now);
|
|
15
|
+
}
|
|
16
|
+
if (config.cacheMs !== undefined) {
|
|
17
|
+
builder.withCache(config.cacheMs);
|
|
18
|
+
}
|
|
19
|
+
if (config.algorithm === "sliding-window") {
|
|
20
|
+
builder.forSlidingWindow({
|
|
21
|
+
limit: config.limit,
|
|
22
|
+
windowMs: config.windowMs,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
builder.forTokenBucket({
|
|
27
|
+
capacity: config.capacity,
|
|
28
|
+
refillRate: config.refillRate,
|
|
29
|
+
refillIntervalMs: config.refillIntervalMs,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return builder.build();
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=create-rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-rate-limiter.js","sourceRoot":"","sources":["../../src/core/create-rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAY/D,MAAM,UAAU,iBAAiB,CAC/B,MAA+B;IAE/B,MAAM,OAAO,GAAG,IAAI,kBAAkB,EAAE,CAAC;IAEzC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,eAAe,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,KAAK,gBAAgB,EAAE,CAAC;QAC1C,OAAO,CAAC,gBAAgB,CAAC;YACvB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC1B,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,cAAc,CAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;SAC1C,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type SlidingWindowConfig } from "../strategies/sliding-window-strategy.js";
|
|
2
|
+
import { type TokenBucketConfig } from "../strategies/token-bucket-strategy.js";
|
|
3
|
+
import type { RateLimitStore } from "../stores/rate-limit-store.js";
|
|
4
|
+
import type { RateLimiterLike, RateLimitStrategy } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Pattern: Builder
|
|
7
|
+
* Problem: Limiter construction spans strategy choice, store choice, key scoping, and optional caching.
|
|
8
|
+
* Solution: The builder makes each choice explicit and validates the combination at build time.
|
|
9
|
+
* Trade-off: More ceremony than a bare constructor; justified because the configuration surface is non-trivial.
|
|
10
|
+
*/
|
|
11
|
+
export declare class RateLimiterBuilder {
|
|
12
|
+
private store;
|
|
13
|
+
private strategy;
|
|
14
|
+
private keyPrefix;
|
|
15
|
+
private cacheTtlMs;
|
|
16
|
+
private now;
|
|
17
|
+
useStore(store: RateLimitStore): this;
|
|
18
|
+
withMemoryStore(): this;
|
|
19
|
+
useStrategy(strategy: RateLimitStrategy<unknown>): this;
|
|
20
|
+
forSlidingWindow(config: SlidingWindowConfig): this;
|
|
21
|
+
forTokenBucket(config: TokenBucketConfig): this;
|
|
22
|
+
withKeyPrefix(prefix: string): this;
|
|
23
|
+
withCache(ttlMs: number): this;
|
|
24
|
+
withClock(now: () => number): this;
|
|
25
|
+
build(): RateLimiterLike;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=rate-limiter-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter-builder.d.ts","sourceRoot":"","sources":["../../src/core/rate-limiter-builder.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,wCAAwC,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEtE;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,GAAG,CAA6B;IAExC,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAKrC,eAAe,IAAI,IAAI;IAKvB,WAAW,CAAC,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,IAAI;IAKvD,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAKnD,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAK/C,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKnC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK9B,SAAS,CAAC,GAAG,EAAE,MAAM,MAAM,GAAG,IAAI;IAKlC,KAAK,IAAI,eAAe;CA+BzB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { RateLimiterConfigurationError } from "../errors.js";
|
|
2
|
+
import { RateLimiter } from "./rate-limiter.js";
|
|
3
|
+
import { CachedRateLimiterProxy } from "./cached-rate-limiter-proxy.js";
|
|
4
|
+
import { SlidingWindowStrategy, } from "../strategies/sliding-window-strategy.js";
|
|
5
|
+
import { TokenBucketStrategy, } from "../strategies/token-bucket-strategy.js";
|
|
6
|
+
import { MemoryStore } from "../stores/memory-store.js";
|
|
7
|
+
/**
|
|
8
|
+
* Pattern: Builder
|
|
9
|
+
* Problem: Limiter construction spans strategy choice, store choice, key scoping, and optional caching.
|
|
10
|
+
* Solution: The builder makes each choice explicit and validates the combination at build time.
|
|
11
|
+
* Trade-off: More ceremony than a bare constructor; justified because the configuration surface is non-trivial.
|
|
12
|
+
*/
|
|
13
|
+
export class RateLimiterBuilder {
|
|
14
|
+
store;
|
|
15
|
+
strategy;
|
|
16
|
+
keyPrefix;
|
|
17
|
+
cacheTtlMs;
|
|
18
|
+
now;
|
|
19
|
+
useStore(store) {
|
|
20
|
+
this.store = store;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
withMemoryStore() {
|
|
24
|
+
this.store = new MemoryStore();
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
useStrategy(strategy) {
|
|
28
|
+
this.strategy = strategy;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
forSlidingWindow(config) {
|
|
32
|
+
this.strategy = new SlidingWindowStrategy(config);
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
forTokenBucket(config) {
|
|
36
|
+
this.strategy = new TokenBucketStrategy(config);
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
withKeyPrefix(prefix) {
|
|
40
|
+
this.keyPrefix = prefix;
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
withCache(ttlMs) {
|
|
44
|
+
this.cacheTtlMs = ttlMs;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
withClock(now) {
|
|
48
|
+
this.now = now;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
build() {
|
|
52
|
+
if (!this.strategy) {
|
|
53
|
+
throw new RateLimiterConfigurationError("RateLimiterBuilder requires a strategy before build().");
|
|
54
|
+
}
|
|
55
|
+
const store = this.store ?? new MemoryStore();
|
|
56
|
+
const options = {};
|
|
57
|
+
if (this.keyPrefix !== undefined) {
|
|
58
|
+
options.keyPrefix = this.keyPrefix;
|
|
59
|
+
}
|
|
60
|
+
if (this.now !== undefined) {
|
|
61
|
+
options.now = this.now;
|
|
62
|
+
}
|
|
63
|
+
const limiter = new RateLimiter(store, this.strategy, options);
|
|
64
|
+
if (this.cacheTtlMs === undefined) {
|
|
65
|
+
return limiter;
|
|
66
|
+
}
|
|
67
|
+
if (!Number.isFinite(this.cacheTtlMs) || this.cacheTtlMs <= 0) {
|
|
68
|
+
throw new RateLimiterConfigurationError("withCache() requires a positive cache TTL in milliseconds.");
|
|
69
|
+
}
|
|
70
|
+
return new CachedRateLimiterProxy(limiter, this.cacheTtlMs, this.now);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=rate-limiter-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter-builder.js","sourceRoot":"","sources":["../../src/core/rate-limiter-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EACL,qBAAqB,GAEtB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,mBAAmB,GAEpB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAIxD;;;;;GAKG;AACH,MAAM,OAAO,kBAAkB;IACrB,KAAK,CAA6B;IAClC,QAAQ,CAAyC;IACjD,SAAS,CAAqB;IAC9B,UAAU,CAAqB;IAC/B,GAAG,CAA6B;IAExC,QAAQ,CAAC,KAAqB;QAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,eAAe;QACb,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,WAAW,CAAC,QAAoC;QAC9C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gBAAgB,CAAC,MAA2B;QAC1C,IAAI,CAAC,QAAQ,GAAG,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,cAAc,CAAC,MAAyB;QACtC,IAAI,CAAC,QAAQ,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa,CAAC,MAAc;QAC1B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,CAAC,GAAiB;QACzB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,IAAI,6BAA6B,CACrC,wDAAwD,CACzD,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,WAAW,EAAE,CAAC;QAC9C,MAAM,OAAO,GAA+C,EAAE,CAAC;QAC/D,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACzB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAE/D,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,6BAA6B,CACrC,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACxE,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RateLimitStore } from "../stores/rate-limit-store.js";
|
|
2
|
+
import type { RateLimitCheckOptions, RateLimitDecision, RateLimiterLike, RateLimitStrategy } from "../types.js";
|
|
3
|
+
export interface RateLimiterOptions {
|
|
4
|
+
keyPrefix?: string;
|
|
5
|
+
now?: () => number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Pattern: Facade
|
|
9
|
+
* Problem: Callers should not orchestrate keys, clocks, stores, and strategies themselves.
|
|
10
|
+
* Solution: RateLimiter exposes a single check/reset API over the underlying subsystem.
|
|
11
|
+
* Trade-off: Core composition is hidden behind one class; justified because the public API should stay small.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RateLimiter<TState> implements RateLimiterLike {
|
|
14
|
+
private readonly store;
|
|
15
|
+
private readonly strategy;
|
|
16
|
+
private readonly now;
|
|
17
|
+
private readonly keyPrefix;
|
|
18
|
+
constructor(store: RateLimitStore, strategy: RateLimitStrategy<TState>, options?: RateLimiterOptions);
|
|
19
|
+
check(key: string, options?: RateLimitCheckOptions): Promise<RateLimitDecision>;
|
|
20
|
+
reset(key: string): Promise<void>;
|
|
21
|
+
private resolveKey;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/core/rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,WAAW,CAAC,MAAM,CAAE,YAAW,eAAe;IAKvD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;gBAG5B,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,EACpD,OAAO,GAAE,kBAAuB;IAM5B,KAAK,CACT,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,iBAAiB,CAAC;IA+BvB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,OAAO,CAAC,UAAU;CAYnB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { RateLimiterConfigurationError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pattern: Facade
|
|
4
|
+
* Problem: Callers should not orchestrate keys, clocks, stores, and strategies themselves.
|
|
5
|
+
* Solution: RateLimiter exposes a single check/reset API over the underlying subsystem.
|
|
6
|
+
* Trade-off: Core composition is hidden behind one class; justified because the public API should stay small.
|
|
7
|
+
*/
|
|
8
|
+
export class RateLimiter {
|
|
9
|
+
store;
|
|
10
|
+
strategy;
|
|
11
|
+
now;
|
|
12
|
+
keyPrefix;
|
|
13
|
+
constructor(store, strategy, options = {}) {
|
|
14
|
+
this.store = store;
|
|
15
|
+
this.strategy = strategy;
|
|
16
|
+
this.now = options.now ?? Date.now;
|
|
17
|
+
this.keyPrefix = options.keyPrefix;
|
|
18
|
+
}
|
|
19
|
+
async check(key, options = {}) {
|
|
20
|
+
const cost = options.cost ?? 1;
|
|
21
|
+
if (!Number.isFinite(cost) || cost <= 0) {
|
|
22
|
+
throw new RateLimiterConfigurationError("check() requires a positive numeric cost.");
|
|
23
|
+
}
|
|
24
|
+
const checkedAtMs = this.now();
|
|
25
|
+
const scopedKey = this.resolveKey(key);
|
|
26
|
+
const snapshot = await this.store.consume(scopedKey, this.strategy, {
|
|
27
|
+
now: checkedAtMs,
|
|
28
|
+
cost,
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
key: scopedKey,
|
|
32
|
+
allowed: snapshot.allowed,
|
|
33
|
+
strategy: this.strategy.kind,
|
|
34
|
+
policy: this.strategy.policy,
|
|
35
|
+
limit: this.strategy.limit,
|
|
36
|
+
used: snapshot.used,
|
|
37
|
+
remaining: snapshot.remaining,
|
|
38
|
+
checkedAt: new Date(checkedAtMs),
|
|
39
|
+
resetAt: new Date(snapshot.resetAt),
|
|
40
|
+
resetAfterMs: Math.max(0, snapshot.resetAt - checkedAtMs),
|
|
41
|
+
retryAfterMs: snapshot.retryAfterMs,
|
|
42
|
+
retryAfterSeconds: Math.ceil(snapshot.retryAfterMs / 1000),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async reset(key) {
|
|
46
|
+
await this.store.reset(this.resolveKey(key));
|
|
47
|
+
}
|
|
48
|
+
resolveKey(key) {
|
|
49
|
+
const trimmed = key.trim();
|
|
50
|
+
if (!trimmed) {
|
|
51
|
+
throw new RateLimiterConfigurationError("Rate limit keys cannot be empty.");
|
|
52
|
+
}
|
|
53
|
+
if (!this.keyPrefix) {
|
|
54
|
+
return trimmed;
|
|
55
|
+
}
|
|
56
|
+
return `${this.keyPrefix}:${trimmed}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/core/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC;AAc7D;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IAKH;IACA;IALF,GAAG,CAAe;IAClB,SAAS,CAAqB;IAE/C,YACmB,KAAqB,EACrB,QAAmC,EACpD,UAA8B,EAAE;QAFf,UAAK,GAAL,KAAK,CAAgB;QACrB,aAAQ,GAAR,QAAQ,CAA2B;QAGpD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK,CACT,GAAW,EACX,UAAiC,EAAE;QAEnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,6BAA6B,CACrC,2CAA2C,CAC5C,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE;YAClE,GAAG,EAAE,WAAW;YAChB,IAAI;SACL,CAAC,CAAC;QAEH,OAAO;YACL,GAAG,EAAE,SAAS;YACd,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;YAC5B,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;YAC5B,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC1B,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,SAAS,EAAE,IAAI,IAAI,CAAC,WAAW,CAAC;YAChC,OAAO,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,WAAW,CAAC;YACzD,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC;SAC3D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACrB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IAEO,UAAU,CAAC,GAAW;QAC5B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,6BAA6B,CAAC,kCAAkC,CAAC,CAAC;QAC9E,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,EAAE,CAAC;IACxC,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RateLimitStrategy } from "../types.js";
|
|
2
|
+
import { type SlidingWindowConfig } from "../strategies/sliding-window-strategy.js";
|
|
3
|
+
import { type TokenBucketConfig } from "../strategies/token-bucket-strategy.js";
|
|
4
|
+
export type StrategyConfig = ({
|
|
5
|
+
algorithm: "sliding-window";
|
|
6
|
+
} & SlidingWindowConfig) | ({
|
|
7
|
+
algorithm: "token-bucket";
|
|
8
|
+
} & TokenBucketConfig);
|
|
9
|
+
/**
|
|
10
|
+
* Pattern: Factory
|
|
11
|
+
* Problem: Callers want a simple config object, not direct knowledge of every strategy class.
|
|
12
|
+
* Solution: The factory centralizes strategy instantiation behind one method.
|
|
13
|
+
* Trade-off: One more creation layer; justified because package ergonomics improve substantially.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RateLimitStrategyFactory {
|
|
16
|
+
static create(config: StrategyConfig): RateLimitStrategy<unknown>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=strategy-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy-factory.d.ts","sourceRoot":"","sources":["../../src/core/strategy-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,wCAAwC,CAAC;AAEhD,MAAM,MAAM,cAAc,GACtB,CAAC;IAAE,SAAS,EAAE,gBAAgB,CAAA;CAAE,GAAG,mBAAmB,CAAC,GACvD,CAAC;IAAE,SAAS,EAAE,cAAc,CAAA;CAAE,GAAG,iBAAiB,CAAC,CAAC;AAExD;;;;;GAKG;AACH,qBAAa,wBAAwB;IACnC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAAC,OAAO,CAAC;CAOlE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SlidingWindowStrategy, } from "../strategies/sliding-window-strategy.js";
|
|
2
|
+
import { TokenBucketStrategy, } from "../strategies/token-bucket-strategy.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pattern: Factory
|
|
5
|
+
* Problem: Callers want a simple config object, not direct knowledge of every strategy class.
|
|
6
|
+
* Solution: The factory centralizes strategy instantiation behind one method.
|
|
7
|
+
* Trade-off: One more creation layer; justified because package ergonomics improve substantially.
|
|
8
|
+
*/
|
|
9
|
+
export class RateLimitStrategyFactory {
|
|
10
|
+
static create(config) {
|
|
11
|
+
if (config.algorithm === "sliding-window") {
|
|
12
|
+
return new SlidingWindowStrategy(config);
|
|
13
|
+
}
|
|
14
|
+
return new TokenBucketStrategy(config);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=strategy-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy-factory.js","sourceRoot":"","sources":["../../src/core/strategy-factory.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,GAEtB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,mBAAmB,GAEpB,MAAM,wCAAwC,CAAC;AAMhD;;;;;GAKG;AACH,MAAM,OAAO,wBAAwB;IACnC,MAAM,CAAC,MAAM,CAAC,MAAsB;QAClC,IAAI,MAAM,CAAC,SAAS,KAAK,gBAAgB,EAAE,CAAC;YAC1C,OAAO,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;CACF"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class RateLimiterConfigurationError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "RateLimiterConfigurationError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class UnsupportedStoreError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "UnsupportedStoreError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IACtD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAC9C,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { RateLimiter } from "./core/rate-limiter.js";
|
|
2
|
+
export { CachedRateLimiterProxy } from "./core/cached-rate-limiter-proxy.js";
|
|
3
|
+
export { createRateLimiter } from "./core/create-rate-limiter.js";
|
|
4
|
+
export { RateLimiterBuilder, } from "./core/rate-limiter-builder.js";
|
|
5
|
+
export { RateLimitStrategyFactory, type StrategyConfig, } from "./core/strategy-factory.js";
|
|
6
|
+
export { SlidingWindowStrategy, type SlidingWindowConfig, type SlidingWindowState, } from "./strategies/sliding-window-strategy.js";
|
|
7
|
+
export { TokenBucketStrategy, type TokenBucketConfig, type TokenBucketState, } from "./strategies/token-bucket-strategy.js";
|
|
8
|
+
export { MemoryStore } from "./stores/memory-store.js";
|
|
9
|
+
export { RedisStore, createIORedisExecutor, createNodeRedisExecutor, type IORedisLikeClient, type NodeRedisLikeClient, type RedisCommandExecutor, } from "./stores/redis-store.js";
|
|
10
|
+
export type { RateLimitStore } from "./stores/rate-limit-store.js";
|
|
11
|
+
export { createExpressRateLimit } from "./adapters/express.js";
|
|
12
|
+
export { createFastifyRateLimit } from "./adapters/fastify.js";
|
|
13
|
+
export { createFetchRateLimit, type FetchLikeHandler, type FetchRateLimitOptions, } from "./adapters/fetch.js";
|
|
14
|
+
export { createHonoRateLimit, type HonoLikeContext, type HonoLikeRequest, type HonoRateLimitOptions, } from "./adapters/hono.js";
|
|
15
|
+
export { createNextRateLimit, type NextRateLimitOptions, type NextRouteHandler, } from "./adapters/next.js";
|
|
16
|
+
export { createNestRateLimitGuard, type NestExecutionContext, type NestHttpArgumentsHost, type NestLikeRequest, type NestLikeResponse, type NestRateLimitOptions, } from "./adapters/nest.js";
|
|
17
|
+
export type { RateLimitCheckOptions, RateLimitDecision, RateLimiterLike, } from "./types.js";
|
|
18
|
+
export { RateLimiterConfigurationError, UnsupportedStoreError, } from "./errors.js";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EACL,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,wBAAwB,EACxB,KAAK,cAAc,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { RateLimiter } from "./core/rate-limiter.js";
|
|
2
|
+
export { CachedRateLimiterProxy } from "./core/cached-rate-limiter-proxy.js";
|
|
3
|
+
export { createRateLimiter } from "./core/create-rate-limiter.js";
|
|
4
|
+
export { RateLimiterBuilder, } from "./core/rate-limiter-builder.js";
|
|
5
|
+
export { RateLimitStrategyFactory, } from "./core/strategy-factory.js";
|
|
6
|
+
export { SlidingWindowStrategy, } from "./strategies/sliding-window-strategy.js";
|
|
7
|
+
export { TokenBucketStrategy, } from "./strategies/token-bucket-strategy.js";
|
|
8
|
+
export { MemoryStore } from "./stores/memory-store.js";
|
|
9
|
+
export { RedisStore, createIORedisExecutor, createNodeRedisExecutor, } from "./stores/redis-store.js";
|
|
10
|
+
export { createExpressRateLimit } from "./adapters/express.js";
|
|
11
|
+
export { createFastifyRateLimit } from "./adapters/fastify.js";
|
|
12
|
+
export { createFetchRateLimit, } from "./adapters/fetch.js";
|
|
13
|
+
export { createHonoRateLimit, } from "./adapters/hono.js";
|
|
14
|
+
export { createNextRateLimit, } from "./adapters/next.js";
|
|
15
|
+
export { createNestRateLimitGuard, } from "./adapters/nest.js";
|
|
16
|
+
export { RateLimiterConfigurationError, UnsupportedStoreError, } from "./errors.js";
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EACL,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,wBAAwB,GAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,GAGtB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,mBAAmB,GAGpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,uBAAuB,GAIxB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,oBAAoB,GAGrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,GAIpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,GAGpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,wBAAwB,GAMzB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RateLimitStore } from "./rate-limit-store.js";
|
|
2
|
+
import type { RateLimitStrategy, StrategyExecutionContext, StrategyExecutionSnapshot } from "../types.js";
|
|
3
|
+
export declare class MemoryStore implements RateLimitStore {
|
|
4
|
+
private readonly entries;
|
|
5
|
+
consume<TState>(key: string, strategy: RateLimitStrategy<TState>, context: StrategyExecutionContext): Promise<StrategyExecutionSnapshot<TState>>;
|
|
6
|
+
reset(key: string): Promise<void>;
|
|
7
|
+
private pruneExpired;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=memory-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/stores/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAOrB,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IAE7D,OAAO,CAAC,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,EACnC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAevC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,OAAO,CAAC,YAAY;CAMrB"}
|