@roneysilva25/test 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/algorithms/Algorithm.interface.d.ts +15 -0
  2. package/dist/algorithms/Algorithm.interface.d.ts.map +1 -0
  3. package/dist/algorithms/Algorithm.interface.js +2 -0
  4. package/dist/algorithms/Algorithm.interface.js.map +1 -0
  5. package/dist/algorithms/FixedWindow.d.ts +10 -0
  6. package/dist/algorithms/FixedWindow.d.ts.map +1 -0
  7. package/dist/algorithms/FixedWindow.interfaces.d.ts +6 -0
  8. package/dist/algorithms/FixedWindow.interfaces.d.ts.map +1 -0
  9. package/dist/algorithms/FixedWindow.interfaces.js +2 -0
  10. package/dist/algorithms/FixedWindow.interfaces.js.map +1 -0
  11. package/dist/algorithms/FixedWindow.js +37 -0
  12. package/dist/algorithms/FixedWindow.js.map +1 -0
  13. package/dist/controllers/RateLimiterController.d.ts +7 -0
  14. package/dist/controllers/RateLimiterController.d.ts.map +1 -0
  15. package/dist/controllers/RateLimiterController.interfaces.d.ts +26 -0
  16. package/dist/controllers/RateLimiterController.interfaces.d.ts.map +1 -0
  17. package/dist/controllers/RateLimiterController.interfaces.js +2 -0
  18. package/dist/controllers/RateLimiterController.interfaces.js.map +1 -0
  19. package/dist/controllers/RateLimiterController.js +17 -0
  20. package/dist/controllers/RateLimiterController.js.map +1 -0
  21. package/dist/db/Storage.d.ts +10 -0
  22. package/dist/db/Storage.d.ts.map +1 -0
  23. package/dist/db/Storage.interfaces.d.ts +6 -0
  24. package/dist/db/Storage.interfaces.d.ts.map +1 -0
  25. package/dist/db/Storage.interfaces.js +2 -0
  26. package/dist/db/Storage.interfaces.js.map +1 -0
  27. package/dist/db/Storage.js +20 -0
  28. package/dist/db/Storage.js.map +1 -0
  29. package/dist/factories/AlgorithmFactory.d.ts +6 -0
  30. package/dist/factories/AlgorithmFactory.d.ts.map +1 -0
  31. package/dist/factories/AlgorithmFactory.interfaces.d.ts +11 -0
  32. package/dist/factories/AlgorithmFactory.interfaces.d.ts.map +1 -0
  33. package/dist/factories/AlgorithmFactory.interfaces.js +2 -0
  34. package/dist/factories/AlgorithmFactory.interfaces.js.map +1 -0
  35. package/dist/factories/AlgorithmFactory.js +12 -0
  36. package/dist/factories/AlgorithmFactory.js.map +1 -0
  37. package/dist/index.d.ts +7 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +48 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/interfaces.d.ts +9 -0
  42. package/dist/interfaces.d.ts.map +1 -0
  43. package/dist/interfaces.js +2 -0
  44. package/dist/interfaces.js.map +1 -0
  45. package/dist/test/FixedWindow.test.d.ts +2 -0
  46. package/dist/test/FixedWindow.test.d.ts.map +1 -0
  47. package/dist/test/FixedWindow.test.js +100 -0
  48. package/dist/test/FixedWindow.test.js.map +1 -0
  49. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ import type { Storage } from "../db/Storage.js";
2
+ import type { PacketPayload } from "./FixedWindow.interfaces.js";
3
+ export interface HandleArgs {
4
+ packetKey: string;
5
+ forwardCb: (packetInfo: PacketPayload) => void;
6
+ dropCb: (packetInfo: PacketPayload) => void;
7
+ }
8
+ export interface Algorithm {
9
+ readonly storage: typeof Storage;
10
+ capacity: number;
11
+ timeWindowInMs: number;
12
+ handle: (args: HandleArgs) => void;
13
+ }
14
+ export type AlgorithmConstructorArgs = Omit<Algorithm, "handle">;
15
+ //# sourceMappingURL=Algorithm.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Algorithm.interface.d.ts","sourceRoot":"","sources":["../../src/algorithms/Algorithm.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,WAAW,UAAU;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,MAAM,EAAC,CAAC,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,OAAO,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Algorithm.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Algorithm.interface.js","sourceRoot":"","sources":["../../src/algorithms/Algorithm.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import { Storage } from "../db/Storage.js";
2
+ import type { HandleArgs, Algorithm, AlgorithmConstructorArgs } from "./Algorithm.interface.js";
3
+ export declare class FixedWindow implements Algorithm {
4
+ capacity: number;
5
+ timeWindowInMs: number;
6
+ storage: typeof Storage;
7
+ constructor({ capacity, timeWindowInMs, storage, }: AlgorithmConstructorArgs);
8
+ handle({ packetKey, forwardCb, dropCb }: HandleArgs): void;
9
+ }
10
+ //# sourceMappingURL=FixedWindow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedWindow.d.ts","sourceRoot":"","sources":["../../src/algorithms/FixedWindow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEhG,qBAAa,WAAY,YAAW,SAAS;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,OAAO,CAAC;gBAEZ,EACR,QAAQ,EACR,cAAc,EACd,OAAO,GACV,EAAE,wBAAwB;IAM3B,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,UAAU;CA8BtD"}
@@ -0,0 +1,6 @@
1
+ interface PacketPayload {
2
+ createdAt: number;
3
+ allowance: number;
4
+ }
5
+ export { type PacketPayload, };
6
+ //# sourceMappingURL=FixedWindow.interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedWindow.interfaces.d.ts","sourceRoot":"","sources":["../../src/algorithms/FixedWindow.interfaces.ts"],"names":[],"mappings":"AAAA,UAAU,aAAa;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,OAAO,EACH,KAAK,aAAa,GACrB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=FixedWindow.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedWindow.interfaces.js","sourceRoot":"","sources":["../../src/algorithms/FixedWindow.interfaces.ts"],"names":[],"mappings":"AAKA,OAAO,EAEN,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { Storage } from "../db/Storage.js";
2
+ export class FixedWindow {
3
+ capacity;
4
+ timeWindowInMs;
5
+ storage;
6
+ constructor({ capacity, timeWindowInMs, storage, }) {
7
+ this.capacity = capacity;
8
+ this.timeWindowInMs = timeWindowInMs;
9
+ this.storage = storage;
10
+ }
11
+ handle({ packetKey, forwardCb, dropCb }) {
12
+ const existingPacket = this.storage.retrieve(packetKey);
13
+ const currentTime = new Date().getTime();
14
+ if (!existingPacket || currentTime - existingPacket.createdAt >= this.timeWindowInMs) {
15
+ const createdPacket = this.storage.store({
16
+ key: packetKey,
17
+ payload: {
18
+ createdAt: currentTime,
19
+ allowance: this.capacity - 1,
20
+ },
21
+ });
22
+ return forwardCb(createdPacket);
23
+ }
24
+ if (existingPacket.allowance - 1 >= 0) {
25
+ const updatedPacket = this.storage.store({
26
+ key: packetKey,
27
+ payload: {
28
+ createdAt: existingPacket.createdAt,
29
+ allowance: existingPacket.allowance - 1,
30
+ },
31
+ });
32
+ return forwardCb(updatedPacket);
33
+ }
34
+ return dropCb(existingPacket);
35
+ }
36
+ }
37
+ //# sourceMappingURL=FixedWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedWindow.js","sourceRoot":"","sources":["../../src/algorithms/FixedWindow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAI3C,MAAM,OAAO,WAAW;IACpB,QAAQ,CAAS;IACjB,cAAc,CAAS;IACvB,OAAO,CAAiB;IAExB,YAAY,EACR,QAAQ,EACR,cAAc,EACd,OAAO,GACgB;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAc;QAC/C,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAgB,SAAS,CAAC,CAAC;QACvE,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAEzC,IAAI,CAAC,cAAc,IAAI,WAAW,GAAG,cAAc,CAAC,SAAS,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACnF,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAgB;gBACpD,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACL,SAAS,EAAE,WAAW;oBACtB,SAAS,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC;iBAC/B;aACJ,CAAC,CAAC;YAEH,OAAO,SAAS,CAAC,aAAa,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,cAAc,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAgB;gBACpD,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACL,SAAS,EAAE,cAAc,CAAC,SAAS;oBACnC,SAAS,EAAE,cAAc,CAAC,SAAS,GAAG,CAAC;iBAC1C;aACJ,CAAC,CAAC;YAEH,OAAO,SAAS,CAAC,aAAa,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,MAAM,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;CACJ"}
@@ -0,0 +1,7 @@
1
+ import type { DropArgs, ForwardArgs } from "./RateLimiterController.interfaces.js";
2
+ export declare class RateLimiterController {
3
+ private configResponseHeaders;
4
+ drop({ res, req, packetInfo, limiterInfo }: DropArgs): import("express").Response<any, Record<string, any>>;
5
+ forward({ req, res, next, packetInfo, limiterInfo }: ForwardArgs): void;
6
+ }
7
+ //# sourceMappingURL=RateLimiterController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimiterController.d.ts","sourceRoot":"","sources":["../../src/controllers/RateLimiterController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAyB,QAAQ,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAE1G,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,qBAAqB;IAOtB,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,QAAQ;IAKpD,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,WAAW;CAI1E"}
@@ -0,0 +1,26 @@
1
+ import type { PacketPayload } from "../algorithms/FixedWindow.interfaces.js";
2
+ import type { NextFunction, Response, Request } from "express";
3
+ interface LimiterInfo {
4
+ capacity: number;
5
+ timeWindowInMs: number;
6
+ }
7
+ interface ConfigResponseHeaders {
8
+ res: Response;
9
+ packetInfo: PacketPayload;
10
+ limiterInfo: LimiterInfo;
11
+ }
12
+ interface DropArgs {
13
+ req: Request;
14
+ res: Response;
15
+ packetInfo: PacketPayload;
16
+ limiterInfo: LimiterInfo;
17
+ }
18
+ interface ForwardArgs {
19
+ req: Request;
20
+ res: Response;
21
+ next: NextFunction;
22
+ packetInfo: PacketPayload;
23
+ limiterInfo: LimiterInfo;
24
+ }
25
+ export type { ConfigResponseHeaders, DropArgs, ForwardArgs, };
26
+ //# sourceMappingURL=RateLimiterController.interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimiterController.interfaces.d.ts","sourceRoot":"","sources":["../../src/controllers/RateLimiterController.interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAG,MAAM,SAAS,CAAC;AAEhE,UAAU,WAAW;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,UAAU,qBAAqB;IAC3B,GAAG,EAAE,QAAQ,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;IAC1B,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED,UAAU,QAAQ;IACd,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;IAC1B,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED,UAAU,WAAW;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,aAAa,CAAC;IAC1B,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED,YAAY,EACR,qBAAqB,EACrB,QAAQ,EACR,WAAW,GACd,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=RateLimiterController.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimiterController.interfaces.js","sourceRoot":"","sources":["../../src/controllers/RateLimiterController.interfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ export class RateLimiterController {
2
+ configResponseHeaders({ limiterInfo, packetInfo, res }) {
3
+ const resetsIn = limiterInfo.timeWindowInMs + packetInfo.createdAt - new Date().getTime();
4
+ res.setHeader("X-RateLimit-Limit", limiterInfo.capacity);
5
+ res.setHeader("X-RateLimit-Remaining", packetInfo.allowance);
6
+ res.setHeader("X-RateLimit-Reset", resetsIn);
7
+ }
8
+ drop({ res, req, packetInfo, limiterInfo }) {
9
+ this.configResponseHeaders({ res, limiterInfo, packetInfo });
10
+ return res.status(429).send();
11
+ }
12
+ forward({ req, res, next, packetInfo, limiterInfo }) {
13
+ this.configResponseHeaders({ res, limiterInfo, packetInfo });
14
+ next();
15
+ }
16
+ }
17
+ //# sourceMappingURL=RateLimiterController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimiterController.js","sourceRoot":"","sources":["../../src/controllers/RateLimiterController.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,qBAAqB;IACtB,qBAAqB,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,EAA0B;QAClF,MAAM,QAAQ,GAAG,WAAW,CAAC,cAAc,GAAG,UAAU,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1F,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QACzD,GAAG,CAAC,SAAS,CAAC,uBAAuB,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;QAC7D,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;IAEM,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAY;QACvD,IAAI,CAAC,qBAAqB,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7D,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IAEM,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAe;QACnE,IAAI,CAAC,qBAAqB,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7D,IAAI,EAAE,CAAC;IACX,CAAC;CACJ"}
@@ -0,0 +1,10 @@
1
+ import type { StoreArgs } from "./Storage.interfaces.js";
2
+ export declare class Storage {
3
+ private static instance;
4
+ private storage;
5
+ private constructor();
6
+ private static getInstance;
7
+ static store<Payload>({ key, payload }: StoreArgs<Payload>): Payload;
8
+ static retrieve<Payload>(key: string): Payload | undefined;
9
+ }
10
+ //# sourceMappingURL=Storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../src/db/Storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,qBAAa,OAAO;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAU;IACjC,OAAO,CAAC,OAAO,CAAmB;IAElC,OAAO;IAIP,OAAO,CAAC,MAAM,CAAC,WAAW;WAQZ,KAAK,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO;WAI7D,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;CAGpE"}
@@ -0,0 +1,6 @@
1
+ interface StoreArgs<Payload> {
2
+ key: string;
3
+ payload: Payload;
4
+ }
5
+ export { type StoreArgs, };
6
+ //# sourceMappingURL=Storage.interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Storage.interfaces.d.ts","sourceRoot":"","sources":["../../src/db/Storage.interfaces.ts"],"names":[],"mappings":"AAAA,UAAU,SAAS,CAAC,OAAO;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,OAAO,EACH,KAAK,SAAS,GACjB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Storage.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Storage.interfaces.js","sourceRoot":"","sources":["../../src/db/Storage.interfaces.ts"],"names":[],"mappings":"AAKA,OAAO,EAEN,CAAA"}
@@ -0,0 +1,20 @@
1
+ export class Storage {
2
+ static instance;
3
+ storage;
4
+ constructor() {
5
+ this.storage = new Map();
6
+ }
7
+ static getInstance() {
8
+ if (!this.instance) {
9
+ this.instance = new Storage();
10
+ }
11
+ return this.instance;
12
+ }
13
+ static store({ key, payload }) {
14
+ return this.getInstance().storage.set(key, payload).get(key);
15
+ }
16
+ static retrieve(key) {
17
+ return this.getInstance().storage.get(key);
18
+ }
19
+ }
20
+ //# sourceMappingURL=Storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Storage.js","sourceRoot":"","sources":["../../src/db/Storage.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,OAAO;IACR,MAAM,CAAC,QAAQ,CAAU;IACzB,OAAO,CAAmB;IAElC;QACI,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;IAC7B,CAAC;IAEO,MAAM,CAAC,WAAW;QACtB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,EAAE,CAAC;QAClC,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAU,EAAE,GAAG,EAAE,OAAO,EAAsB;QAC7D,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjE,CAAC;IAEM,MAAM,CAAC,QAAQ,CAAU,GAAW;QACvC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;CACJ"}
@@ -0,0 +1,6 @@
1
+ import type { Algorithm } from "../algorithms/Algorithm.interface.js";
2
+ import type { GetArgs, IAlgorithmFactory } from "./AlgorithmFactory.interfaces.js";
3
+ export declare class AlgorithmFactory implements IAlgorithmFactory {
4
+ get({ algorithm, config }: GetArgs): Algorithm;
5
+ }
6
+ //# sourceMappingURL=AlgorithmFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlgorithmFactory.d.ts","sourceRoot":"","sources":["../../src/factories/AlgorithmFactory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAEnF,qBAAa,gBAAiB,YAAW,iBAAiB;IAC/C,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,GAAG,SAAS;CAQxD"}
@@ -0,0 +1,11 @@
1
+ import type { Algorithm, AlgorithmConstructorArgs } from "../algorithms/Algorithm.interface.js";
2
+ type Algorithms = "fixed_window";
3
+ interface GetArgs {
4
+ algorithm: Algorithms;
5
+ config: AlgorithmConstructorArgs;
6
+ }
7
+ interface IAlgorithmFactory {
8
+ get(args: GetArgs): Algorithm;
9
+ }
10
+ export { type IAlgorithmFactory, type GetArgs, };
11
+ //# sourceMappingURL=AlgorithmFactory.interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlgorithmFactory.interfaces.d.ts","sourceRoot":"","sources":["../../src/factories/AlgorithmFactory.interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAEhG,KAAK,UAAU,GAAG,cAAc,CAAC;AAEjC,UAAU,OAAO;IACb,SAAS,EAAE,UAAU,CAAC;IACtB,MAAM,EAAE,wBAAwB,CAAC;CACpC;AAED,UAAU,iBAAiB;IACvB,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;CACjC;AAED,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,OAAO,GACf,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AlgorithmFactory.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlgorithmFactory.interfaces.js","sourceRoot":"","sources":["../../src/factories/AlgorithmFactory.interfaces.ts"],"names":[],"mappings":"AAaA,OAAO,EAGN,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { FixedWindow } from "../algorithms/FixedWindow.js";
2
+ export class AlgorithmFactory {
3
+ get({ algorithm, config }) {
4
+ switch (algorithm) {
5
+ case "fixed_window":
6
+ return new FixedWindow(config);
7
+ default:
8
+ throw new Error(`Unknown algorithm: ${algorithm}.`);
9
+ }
10
+ }
11
+ }
12
+ //# sourceMappingURL=AlgorithmFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlgorithmFactory.js","sourceRoot":"","sources":["../../src/factories/AlgorithmFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,MAAM,OAAO,gBAAgB;IAClB,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAW;QACrC,QAAQ,SAAS,EAAE,CAAC;YAChB,KAAK,cAAc;gBACf,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;YACnC;gBACI,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,GAAG,CAAC,CAAC;QAC5D,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,7 @@
1
+ import type { NextFunction, Request, Response } from "express";
2
+ import type { RateLimiterArgs } from "./interfaces.js";
3
+ declare function rateLimiter({ algorithm, capacity, timeWindowInMs, storage }: RateLimiterArgs): {
4
+ limit(req: Request, res: Response, next: NextFunction): void;
5
+ };
6
+ export { rateLimiter, };
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAQvD,iBAAS,WAAW,CAAC,EACjB,SAAS,EACT,QAAQ,EACR,cAAc,EACd,OAAiB,EACpB,EAAE,eAAe;eAYC,OAAO,OAAO,QAAQ,QAAQ,YAAY;EA2B5D;AAED,OAAO,EACH,WAAW,GACd,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ import { Storage } from "./db/Storage.js";
2
+ import { RateLimiterController } from "./controllers/RateLimiterController.js";
3
+ import { AlgorithmFactory } from "./factories/AlgorithmFactory.js";
4
+ function validateIP(req, res) {
5
+ if (!req.ip) {
6
+ return res.status(400).send("Invalid IP Address.");
7
+ }
8
+ }
9
+ function rateLimiter({ algorithm, capacity, timeWindowInMs, storage = Storage }) {
10
+ const controller = new RateLimiterController();
11
+ const algFactory = new AlgorithmFactory().get({
12
+ algorithm,
13
+ config: {
14
+ timeWindowInMs,
15
+ storage,
16
+ capacity,
17
+ },
18
+ });
19
+ return {
20
+ limit(req, res, next) {
21
+ validateIP(req, res);
22
+ return algFactory.handle({
23
+ packetKey: req.ip,
24
+ dropCb: (packetInfo) => controller.drop({
25
+ req,
26
+ res,
27
+ packetInfo,
28
+ limiterInfo: {
29
+ capacity,
30
+ timeWindowInMs,
31
+ },
32
+ }),
33
+ forwardCb: (packetInfo) => controller.forward({
34
+ req,
35
+ res,
36
+ next,
37
+ packetInfo,
38
+ limiterInfo: {
39
+ capacity,
40
+ timeWindowInMs,
41
+ },
42
+ }),
43
+ });
44
+ }
45
+ };
46
+ }
47
+ export { rateLimiter, };
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGnE,SAAS,UAAU,CAAC,GAAY,EAAE,GAAa;IAC3C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACV,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACvD,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,EACjB,SAAS,EACT,QAAQ,EACR,cAAc,EACd,OAAO,GAAG,OAAO,EACH;IACd,MAAM,UAAU,GAAG,IAAI,qBAAqB,EAAE,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,gBAAgB,EAAE,CAAC,GAAG,CAAC;QAC1C,SAAS;QACT,MAAM,EAAE;YACJ,cAAc;YACd,OAAO;YACP,QAAQ;SACX;KACJ,CAAC,CAAC;IAEH,OAAO;QACH,KAAK,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;YACjD,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAErB,OAAO,UAAU,CAAC,MAAM,CAAC;gBACrB,SAAS,EAAE,GAAG,CAAC,EAAY;gBAC3B,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;oBACpC,GAAG;oBACH,GAAG;oBACH,UAAU;oBACV,WAAW,EAAE;wBACT,QAAQ;wBACR,cAAc;qBACjB;iBACJ,CAAC;gBACF,SAAS,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;oBAC1C,GAAG;oBACH,GAAG;oBACH,IAAI;oBACJ,UAAU;oBACV,WAAW,EAAE;wBACT,QAAQ;wBACR,cAAc;qBACjB;iBACJ,CAAC;aACL,CAAC,CAAC;QACP,CAAC;KACJ,CAAA;AACL,CAAC;AAED,OAAO,EACH,WAAW,GACd,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { Storage } from "./db/Storage.js";
2
+ interface RateLimiterArgs {
3
+ algorithm: "fixed_window";
4
+ capacity: number;
5
+ timeWindowInMs: number;
6
+ storage?: typeof Storage;
7
+ }
8
+ export type { RateLimiterArgs };
9
+ //# sourceMappingURL=interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,UAAU,eAAe;IACrB,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,OAAO,CAAC;CAC5B;AAED,YAAY,EACR,eAAe,EAClB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=FixedWindow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedWindow.test.d.ts","sourceRoot":"","sources":["../../src/test/FixedWindow.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ import { FixedWindow } from "../algorithms/FixedWindow.js";
2
+ describe("FixedWindow", () => {
3
+ afterEach(() => {
4
+ jest.clearAllMocks();
5
+ });
6
+ const mockGetTime = jest.fn();
7
+ const mockStore = jest.fn();
8
+ const mockRetrieve = jest.fn();
9
+ const mockStorage = {
10
+ store: mockStore,
11
+ retrieve: mockRetrieve,
12
+ };
13
+ const mockDropCb = jest.fn();
14
+ const mockForwardCb = jest.fn();
15
+ const fixedWindowArgs = {
16
+ capacity: 200,
17
+ timeWindowInMs: 1000 * 60 * 2,
18
+ storage: mockStorage,
19
+ };
20
+ //@ts-ignore
21
+ const fixedWindow = new FixedWindow(fixedWindowArgs);
22
+ jest.spyOn(Date.prototype, "getTime").mockImplementation(mockGetTime);
23
+ it("should create a packet with maximum allowance, the current timestamp and call forwardCb, all if the packet does not exist yet ", () => {
24
+ const createdPacket = {
25
+ key: "packetKey",
26
+ payload: {
27
+ createdAt: 1,
28
+ allowance: fixedWindowArgs.capacity - 1,
29
+ },
30
+ };
31
+ mockGetTime.mockReturnValueOnce(1);
32
+ mockRetrieve.mockReturnValueOnce(undefined);
33
+ mockStore.mockReturnValueOnce(createdPacket);
34
+ fixedWindow.handle({
35
+ packetKey: "packetKey",
36
+ dropCb: mockDropCb,
37
+ forwardCb: mockForwardCb,
38
+ });
39
+ expect(mockStore).toHaveBeenCalledWith(createdPacket);
40
+ expect(mockForwardCb).toHaveBeenCalledWith(createdPacket);
41
+ });
42
+ it("should update an existing packet with reset allowance and timestamp, and call forwardCb if the time window has passed", () => {
43
+ const resetAllowance = fixedWindowArgs.capacity - 1;
44
+ const updatedPacket = {
45
+ createdAt: fixedWindowArgs.timeWindowInMs,
46
+ allowance: resetAllowance,
47
+ };
48
+ const currentTimestamp = fixedWindowArgs.timeWindowInMs;
49
+ mockGetTime.mockReturnValueOnce(currentTimestamp);
50
+ mockRetrieve.mockReturnValueOnce({
51
+ createdAt: 0,
52
+ allowance: 10,
53
+ });
54
+ mockStore.mockReturnValueOnce(updatedPacket);
55
+ fixedWindow.handle({
56
+ packetKey: "packetKey",
57
+ dropCb: mockDropCb,
58
+ forwardCb: mockForwardCb,
59
+ });
60
+ expect(mockForwardCb).toHaveBeenCalledWith(updatedPacket);
61
+ });
62
+ it("should update an existing packet, decreasing its allowance by 1 and keeping the same createdAt timestamp, when the allowance has not reached 0 (zero)", () => {
63
+ const foundPacket = {
64
+ createdAt: 10,
65
+ allowance: 4,
66
+ };
67
+ const updatedPacket = {
68
+ createdAt: foundPacket.createdAt,
69
+ allowance: 3,
70
+ };
71
+ mockGetTime.mockReturnValueOnce(foundPacket.createdAt + 1);
72
+ mockRetrieve.mockReturnValueOnce(foundPacket);
73
+ mockStore.mockReturnValueOnce(updatedPacket);
74
+ fixedWindow.handle({
75
+ packetKey: "packetKey",
76
+ dropCb: mockDropCb,
77
+ forwardCb: mockForwardCb,
78
+ });
79
+ expect(mockStore).toHaveBeenCalledWith({
80
+ key: "packetKey",
81
+ payload: updatedPacket,
82
+ });
83
+ expect(mockForwardCb).toHaveBeenCalledWith(updatedPacket);
84
+ });
85
+ it("should call the dropCb when the allowance has already reached zero.", () => {
86
+ const foundPacket = {
87
+ createdAt: 10,
88
+ allowance: 0,
89
+ };
90
+ mockGetTime.mockReturnValueOnce(foundPacket.createdAt + 10);
91
+ mockRetrieve.mockReturnValueOnce(foundPacket);
92
+ fixedWindow.handle({
93
+ packetKey: "packetKey",
94
+ dropCb: mockDropCb,
95
+ forwardCb: mockForwardCb,
96
+ });
97
+ expect(mockDropCb).toHaveBeenCalledWith(foundPacket);
98
+ });
99
+ });
100
+ //# sourceMappingURL=FixedWindow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedWindow.test.js","sourceRoot":"","sources":["../../src/test/FixedWindow.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roneysilva25/test",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Implementation of the most common rate limiting algorithms: Fixed Window, Sliding Window, Leaky Bucket, Token Bucket. To be used as middleware for express applications.",
5
5
  "homepage": "https://github.com/roneysilva25/rate-limiter#readme",
6
6
  "bugs": {