ratelimit-flex 1.0.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +274 -0
  4. package/dist/cjs/index.d.ts +32 -0
  5. package/dist/cjs/index.d.ts.map +1 -0
  6. package/dist/cjs/index.js +73 -0
  7. package/dist/cjs/index.js.map +1 -0
  8. package/dist/cjs/middleware/express.d.ts +18 -0
  9. package/dist/cjs/middleware/express.d.ts.map +1 -0
  10. package/dist/cjs/middleware/express.js +61 -0
  11. package/dist/cjs/middleware/express.js.map +1 -0
  12. package/dist/cjs/middleware/fastify.d.ts +21 -0
  13. package/dist/cjs/middleware/fastify.d.ts.map +1 -0
  14. package/dist/cjs/middleware/fastify.js +66 -0
  15. package/dist/cjs/middleware/fastify.js.map +1 -0
  16. package/dist/cjs/middleware/index.d.ts +4 -0
  17. package/dist/cjs/middleware/index.d.ts.map +1 -0
  18. package/dist/cjs/middleware/index.js +9 -0
  19. package/dist/cjs/middleware/index.js.map +1 -0
  20. package/dist/cjs/middleware/merge-options.d.ts +16 -0
  21. package/dist/cjs/middleware/merge-options.d.ts.map +1 -0
  22. package/dist/cjs/middleware/merge-options.js +71 -0
  23. package/dist/cjs/middleware/merge-options.js.map +1 -0
  24. package/dist/cjs/package.json +1 -0
  25. package/dist/cjs/stores/index.d.ts +4 -0
  26. package/dist/cjs/stores/index.d.ts.map +1 -0
  27. package/dist/cjs/stores/index.js +11 -0
  28. package/dist/cjs/stores/index.js.map +1 -0
  29. package/dist/cjs/stores/memory-store.d.ts +74 -0
  30. package/dist/cjs/stores/memory-store.d.ts.map +1 -0
  31. package/dist/cjs/stores/memory-store.js +259 -0
  32. package/dist/cjs/stores/memory-store.js.map +1 -0
  33. package/dist/cjs/stores/redis-store.d.ts +113 -0
  34. package/dist/cjs/stores/redis-store.d.ts.map +1 -0
  35. package/dist/cjs/stores/redis-store.js +452 -0
  36. package/dist/cjs/stores/redis-store.js.map +1 -0
  37. package/dist/cjs/strategies/defaults.d.ts +28 -0
  38. package/dist/cjs/strategies/defaults.d.ts.map +1 -0
  39. package/dist/cjs/strategies/defaults.js +31 -0
  40. package/dist/cjs/strategies/defaults.js.map +1 -0
  41. package/dist/cjs/strategies/index.d.ts +4 -0
  42. package/dist/cjs/strategies/index.d.ts.map +1 -0
  43. package/dist/cjs/strategies/index.js +13 -0
  44. package/dist/cjs/strategies/index.js.map +1 -0
  45. package/dist/cjs/strategies/rate-limit-engine.d.ts +42 -0
  46. package/dist/cjs/strategies/rate-limit-engine.d.ts.map +1 -0
  47. package/dist/cjs/strategies/rate-limit-engine.js +128 -0
  48. package/dist/cjs/strategies/rate-limit-engine.js.map +1 -0
  49. package/dist/cjs/types/index.d.ts +93 -0
  50. package/dist/cjs/types/index.d.ts.map +1 -0
  51. package/dist/cjs/types/index.js +14 -0
  52. package/dist/cjs/types/index.js.map +1 -0
  53. package/dist/cjs/utils/index.d.ts +3 -0
  54. package/dist/cjs/utils/index.d.ts.map +1 -0
  55. package/dist/cjs/utils/index.js +4 -0
  56. package/dist/cjs/utils/index.js.map +1 -0
  57. package/dist/index.d.ts +32 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +43 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/middleware/express.d.ts +18 -0
  62. package/dist/middleware/express.d.ts.map +1 -0
  63. package/dist/middleware/express.js +58 -0
  64. package/dist/middleware/express.js.map +1 -0
  65. package/dist/middleware/fastify.d.ts +21 -0
  66. package/dist/middleware/fastify.d.ts.map +1 -0
  67. package/dist/middleware/fastify.js +60 -0
  68. package/dist/middleware/fastify.js.map +1 -0
  69. package/dist/middleware/index.d.ts +4 -0
  70. package/dist/middleware/index.d.ts.map +1 -0
  71. package/dist/middleware/index.js +4 -0
  72. package/dist/middleware/index.js.map +1 -0
  73. package/dist/middleware/merge-options.d.ts +16 -0
  74. package/dist/middleware/merge-options.d.ts.map +1 -0
  75. package/dist/middleware/merge-options.js +64 -0
  76. package/dist/middleware/merge-options.js.map +1 -0
  77. package/dist/stores/index.d.ts +4 -0
  78. package/dist/stores/index.d.ts.map +1 -0
  79. package/dist/stores/index.js +4 -0
  80. package/dist/stores/index.js.map +1 -0
  81. package/dist/stores/memory-store.d.ts +74 -0
  82. package/dist/stores/memory-store.d.ts.map +1 -0
  83. package/dist/stores/memory-store.js +255 -0
  84. package/dist/stores/memory-store.js.map +1 -0
  85. package/dist/stores/redis-store.d.ts +113 -0
  86. package/dist/stores/redis-store.d.ts.map +1 -0
  87. package/dist/stores/redis-store.js +413 -0
  88. package/dist/stores/redis-store.js.map +1 -0
  89. package/dist/strategies/defaults.d.ts +28 -0
  90. package/dist/strategies/defaults.d.ts.map +1 -0
  91. package/dist/strategies/defaults.js +28 -0
  92. package/dist/strategies/defaults.js.map +1 -0
  93. package/dist/strategies/index.d.ts +4 -0
  94. package/dist/strategies/index.d.ts.map +1 -0
  95. package/dist/strategies/index.js +4 -0
  96. package/dist/strategies/index.js.map +1 -0
  97. package/dist/strategies/rate-limit-engine.d.ts +42 -0
  98. package/dist/strategies/rate-limit-engine.d.ts.map +1 -0
  99. package/dist/strategies/rate-limit-engine.js +122 -0
  100. package/dist/strategies/rate-limit-engine.js.map +1 -0
  101. package/dist/types/index.d.ts +93 -0
  102. package/dist/types/index.d.ts.map +1 -0
  103. package/dist/types/index.js +11 -0
  104. package/dist/types/index.js.map +1 -0
  105. package/dist/utils/index.d.ts +3 -0
  106. package/dist/utils/index.d.ts.map +1 -0
  107. package/dist/utils/index.js +3 -0
  108. package/dist/utils/index.js.map +1 -0
  109. package/package.json +90 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-options.js","sourceRoot":"","sources":["../../src/middleware/merge-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,GAAG;IACf,OAAO,EAAE,mBAAmB;IAC5B,kBAAkB,EAAE,KAAK;IACzB,sBAAsB,EAAE,KAAK;CACrB,CAAC;AAEX,MAAM,UAAU,QAAQ,CAAC,IAAsB;IAC7C,IAAI,IAAI,CAAC,QAAQ,KAAK,iBAAiB,CAAC,YAAY,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAkC;IACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC,cAAc,CAAC;IAEtE,IAAI,QAAQ,KAAK,iBAAiB,CAAC,YAAY,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG;YACb,GAAG,mBAAmB;YACtB,GAAG,YAAY;YACf,GAAG,OAAO;YACV,QAAQ,EAAE,iBAAiB,CAAC,YAAqB;SAClD,CAAC;QACF,MAAM,KAAK,GACT,MAAM,CAAC,KAAK;YACZ,IAAI,WAAW,CAAC;gBACd,QAAQ,EAAE,iBAAiB,CAAC,YAAY;gBACxC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;gBAC3C,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC,CAAC;QACL,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,cAAc,GAClB,QAAQ,KAAK,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,qBAAqB,CAAC;IAE5F,MAAM,MAAM,GAAG;QACb,GAAG,cAAc;QACjB,GAAG,YAAY;QACf,GAAG,OAAO;QACV,QAAQ;KACT,CAAC;IAEF,MAAM,KAAK,GACT,MAAM,CAAC,KAAK;QACZ,IAAI,WAAW,CAAC;YACd,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,KAAM;YACnC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,GAAG;SACvC,CAAC,CAAC;IAEL,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAsB,EAAE,MAAuB;IAC7E,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC,SAAS;QACzB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** Backing stores for rate limit state */
2
+ export { MemoryStore, type MemoryStoreOptions, type MemoryStoreTokenBucketOptions, type MemoryStoreWindowOptions, } from './memory-store.js';
3
+ export { RedisStore, adaptIoRedisClient, adaptNodeRedisClient, type RedisLikeClient, type RedisStoreOptions, type RedisStoreStrategyOptions, type RedisStoreTokenBucketOptions, type RedisStoreWindowOptions, } from './redis-store.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,GAC7B,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** Backing stores for rate limit state */
2
+ export { MemoryStore, } from './memory-store.js';
3
+ export { RedisStore, adaptIoRedisClient, adaptNodeRedisClient, } from './redis-store.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,OAAO,EACL,WAAW,GAIZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,oBAAoB,GAMrB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,74 @@
1
+ import type { RateLimitResult, RateLimitStore } from '../types/index.js';
2
+ import { RateLimitStrategy } from '../types/index.js';
3
+ /** Options for window-based strategies (sliding / fixed). */
4
+ export type MemoryStoreWindowOptions = {
5
+ strategy: RateLimitStrategy.SLIDING_WINDOW | RateLimitStrategy.FIXED_WINDOW;
6
+ /** Length of the rate-limit window in milliseconds. */
7
+ windowMs: number;
8
+ /** Maximum number of requests allowed per window. */
9
+ maxRequests: number;
10
+ };
11
+ /** Options for the token-bucket strategy. */
12
+ export type MemoryStoreTokenBucketOptions = {
13
+ strategy: RateLimitStrategy.TOKEN_BUCKET;
14
+ /** Tokens added on each refill interval. */
15
+ tokensPerInterval: number;
16
+ /** Refill interval length in milliseconds. */
17
+ interval: number;
18
+ /** Maximum tokens (burst capacity). */
19
+ bucketSize: number;
20
+ };
21
+ export type MemoryStoreOptions = MemoryStoreWindowOptions | MemoryStoreTokenBucketOptions;
22
+ /**
23
+ * In-memory {@link RateLimitStore} with per-strategy algorithms.
24
+ *
25
+ * - **Sliding window**: keeps request timestamps per key; counts only those inside `windowMs`.
26
+ * - **Fixed window**: stores a counter and window end time; resets when the window expires.
27
+ * - **Token bucket**: refills tokens on a schedule, then consumes one token per request.
28
+ *
29
+ * A background timer runs periodically to drop stale keys / trim old timestamps.
30
+ */
31
+ export declare class MemoryStore implements RateLimitStore {
32
+ private readonly strategy;
33
+ private readonly windowMs;
34
+ private readonly maxRequests;
35
+ private readonly tokensPerInterval;
36
+ private readonly refillIntervalMs;
37
+ private readonly bucketSize;
38
+ /** How often the background purge runs (ms). */
39
+ private readonly cleanupEveryMs;
40
+ /** Request timestamps (sliding window only). */
41
+ private readonly sliding;
42
+ /** Counter + window end (fixed window only). */
43
+ private readonly fixed;
44
+ /** Token bucket state (token bucket only). */
45
+ private readonly buckets;
46
+ private cleanupTimer;
47
+ constructor(options: MemoryStoreOptions);
48
+ /** @inheritdoc */
49
+ increment(key: string): Promise<RateLimitResult>;
50
+ /** @inheritdoc */
51
+ decrement(key: string): Promise<void>;
52
+ /** @inheritdoc */
53
+ reset(key: string): Promise<void>;
54
+ /** @inheritdoc */
55
+ shutdown(): Promise<void>;
56
+ private incrementSliding;
57
+ private decrementSliding;
58
+ private incrementFixed;
59
+ private decrementFixed;
60
+ private incrementTokenBucket;
61
+ private decrementTokenBucket;
62
+ /**
63
+ * Drops stale keys and trims sliding-window timestamps.
64
+ * Runs on the background interval and can be invoked after mutations if needed.
65
+ */
66
+ private purgeExpired;
67
+ private purgeSliding;
68
+ private purgeFixed;
69
+ /**
70
+ * Remove idle full buckets to cap memory (activity is tracked via `lastRefill`).
71
+ */
72
+ private purgeBuckets;
73
+ }
74
+ //# 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,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,6DAA6D;AAC7D,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,iBAAiB,CAAC,cAAc,GAAG,iBAAiB,CAAC,YAAY,CAAC;IAC5E,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,EAAE,iBAAiB,CAAC,YAAY,CAAC;IACzC,4CAA4C;IAC5C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,wBAAwB,GAAG,6BAA6B,CAAC;AAK1F;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IAEvD,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IAEvD,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,YAAY,CAA6C;gBAErD,OAAO,EAAE,kBAAkB;IAkCvC,kBAAkB;IACZ,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAetD,kBAAkB;IACZ,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB3C,kBAAkB;IACZ,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvC,kBAAkB;IACZ,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAa/B,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,oBAAoB;IA8C5B,OAAO,CAAC,oBAAoB;IAW5B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACH,OAAO,CAAC,YAAY;CAQrB"}
@@ -0,0 +1,255 @@
1
+ import { RateLimitStrategy } from '../types/index.js';
2
+ /**
3
+ * In-memory {@link RateLimitStore} with per-strategy algorithms.
4
+ *
5
+ * - **Sliding window**: keeps request timestamps per key; counts only those inside `windowMs`.
6
+ * - **Fixed window**: stores a counter and window end time; resets when the window expires.
7
+ * - **Token bucket**: refills tokens on a schedule, then consumes one token per request.
8
+ *
9
+ * A background timer runs periodically to drop stale keys / trim old timestamps.
10
+ */
11
+ export class MemoryStore {
12
+ constructor(options) {
13
+ /** Request timestamps (sliding window only). */
14
+ this.sliding = new Map();
15
+ /** Counter + window end (fixed window only). */
16
+ this.fixed = new Map();
17
+ /** Token bucket state (token bucket only). */
18
+ this.buckets = new Map();
19
+ this.strategy = options.strategy;
20
+ if (options.strategy === RateLimitStrategy.TOKEN_BUCKET) {
21
+ this.windowMs = 0;
22
+ this.maxRequests = 0;
23
+ this.tokensPerInterval = options.tokensPerInterval;
24
+ this.refillIntervalMs = options.interval;
25
+ this.bucketSize = options.bucketSize;
26
+ // Align cleanup with refill cadence; avoids a separate windowMs for bucket mode.
27
+ this.cleanupEveryMs = Math.max(1, options.interval);
28
+ }
29
+ else {
30
+ this.windowMs = options.windowMs;
31
+ this.maxRequests = options.maxRequests;
32
+ this.tokensPerInterval = 0;
33
+ this.refillIntervalMs = 0;
34
+ this.bucketSize = 0;
35
+ this.cleanupEveryMs = Math.max(1, options.windowMs);
36
+ }
37
+ this.cleanupTimer = setInterval(() => {
38
+ this.purgeExpired();
39
+ }, this.cleanupEveryMs);
40
+ // Do not keep the process alive solely because of this timer (Node.js).
41
+ if (typeof this.cleanupTimer === 'object' &&
42
+ this.cleanupTimer !== null &&
43
+ 'unref' in this.cleanupTimer) {
44
+ this.cleanupTimer.unref();
45
+ }
46
+ }
47
+ /** @inheritdoc */
48
+ async increment(key) {
49
+ switch (this.strategy) {
50
+ case RateLimitStrategy.SLIDING_WINDOW:
51
+ return Promise.resolve(this.incrementSliding(key));
52
+ case RateLimitStrategy.FIXED_WINDOW:
53
+ return Promise.resolve(this.incrementFixed(key));
54
+ case RateLimitStrategy.TOKEN_BUCKET:
55
+ return Promise.resolve(this.incrementTokenBucket(key));
56
+ default: {
57
+ const exhaustive = this.strategy;
58
+ return Promise.reject(new Error(`Unsupported strategy: ${String(exhaustive)}`));
59
+ }
60
+ }
61
+ }
62
+ /** @inheritdoc */
63
+ async decrement(key) {
64
+ switch (this.strategy) {
65
+ case RateLimitStrategy.SLIDING_WINDOW:
66
+ this.decrementSliding(key);
67
+ break;
68
+ case RateLimitStrategy.FIXED_WINDOW:
69
+ this.decrementFixed(key);
70
+ break;
71
+ case RateLimitStrategy.TOKEN_BUCKET:
72
+ this.decrementTokenBucket(key);
73
+ break;
74
+ default: {
75
+ const exhaustive = this.strategy;
76
+ throw new Error(`Unsupported strategy: ${String(exhaustive)}`);
77
+ }
78
+ }
79
+ return Promise.resolve();
80
+ }
81
+ /** @inheritdoc */
82
+ async reset(key) {
83
+ this.sliding.delete(key);
84
+ this.fixed.delete(key);
85
+ this.buckets.delete(key);
86
+ return Promise.resolve();
87
+ }
88
+ /** @inheritdoc */
89
+ async shutdown() {
90
+ if (this.cleanupTimer !== undefined) {
91
+ clearInterval(this.cleanupTimer);
92
+ this.cleanupTimer = undefined;
93
+ }
94
+ this.sliding.clear();
95
+ this.fixed.clear();
96
+ this.buckets.clear();
97
+ return Promise.resolve();
98
+ }
99
+ // --- Sliding window -----------------------------------------------------
100
+ incrementSliding(key) {
101
+ const now = Date.now();
102
+ const cutoff = now - this.windowMs;
103
+ const prev = this.sliding.get(key) ?? [];
104
+ const trimmed = prev.filter((ts) => ts > cutoff);
105
+ trimmed.push(now);
106
+ this.sliding.set(key, trimmed);
107
+ const totalHits = trimmed.length;
108
+ const isBlocked = totalHits > this.maxRequests;
109
+ const remaining = isBlocked ? 0 : Math.max(0, this.maxRequests - totalHits);
110
+ const oldest = trimmed[0];
111
+ const resetTime = new Date(oldest !== undefined ? oldest + this.windowMs : now + this.windowMs);
112
+ return { totalHits, remaining, resetTime, isBlocked };
113
+ }
114
+ decrementSliding(key) {
115
+ const ts = this.sliding.get(key);
116
+ if (!ts || ts.length === 0) {
117
+ return;
118
+ }
119
+ ts.pop();
120
+ if (ts.length === 0) {
121
+ this.sliding.delete(key);
122
+ }
123
+ else {
124
+ this.sliding.set(key, ts);
125
+ }
126
+ }
127
+ // --- Fixed window ---------------------------------------------------------
128
+ incrementFixed(key) {
129
+ const now = Date.now();
130
+ let entry = this.fixed.get(key);
131
+ if (!entry || now >= entry.resetTime) {
132
+ entry = { count: 1, resetTime: now + this.windowMs };
133
+ }
134
+ else {
135
+ entry = { count: entry.count + 1, resetTime: entry.resetTime };
136
+ }
137
+ this.fixed.set(key, entry);
138
+ const totalHits = entry.count;
139
+ const isBlocked = totalHits > this.maxRequests;
140
+ const remaining = isBlocked ? 0 : Math.max(0, this.maxRequests - totalHits);
141
+ const resetTime = new Date(entry.resetTime);
142
+ return { totalHits, remaining, resetTime, isBlocked };
143
+ }
144
+ decrementFixed(key) {
145
+ const entry = this.fixed.get(key);
146
+ if (!entry || entry.count <= 0) {
147
+ return;
148
+ }
149
+ const next = entry.count - 1;
150
+ if (next <= 0) {
151
+ this.fixed.delete(key);
152
+ }
153
+ else {
154
+ this.fixed.set(key, { count: next, resetTime: entry.resetTime });
155
+ }
156
+ }
157
+ // --- Token bucket ---------------------------------------------------------
158
+ incrementTokenBucket(key) {
159
+ const now = Date.now();
160
+ let state = this.buckets.get(key);
161
+ if (!state) {
162
+ state = { tokens: this.bucketSize, lastRefill: now };
163
+ }
164
+ let { tokens, lastRefill } = state;
165
+ // Refill in whole intervals so `lastRefill` stays aligned with the schedule.
166
+ const elapsed = now - lastRefill;
167
+ const intervals = Math.floor(elapsed / this.refillIntervalMs);
168
+ if (intervals > 0) {
169
+ tokens = Math.min(this.bucketSize, tokens + intervals * this.tokensPerInterval);
170
+ lastRefill += intervals * this.refillIntervalMs;
171
+ }
172
+ if (tokens >= 1) {
173
+ tokens -= 1;
174
+ this.buckets.set(key, { tokens, lastRefill });
175
+ const remaining = tokens;
176
+ const totalHits = this.bucketSize - remaining;
177
+ const resetTime = new Date(lastRefill + this.refillIntervalMs);
178
+ return {
179
+ totalHits,
180
+ remaining,
181
+ resetTime,
182
+ isBlocked: false,
183
+ };
184
+ }
185
+ // Blocked: next token arrives at the upcoming refill boundary.
186
+ const nextRefillAt = lastRefill + this.refillIntervalMs;
187
+ this.buckets.set(key, { tokens, lastRefill });
188
+ return {
189
+ totalHits: this.bucketSize,
190
+ remaining: 0,
191
+ resetTime: new Date(nextRefillAt),
192
+ isBlocked: true,
193
+ };
194
+ }
195
+ decrementTokenBucket(key) {
196
+ const state = this.buckets.get(key);
197
+ if (!state) {
198
+ return;
199
+ }
200
+ const tokens = Math.min(this.bucketSize, state.tokens + 1);
201
+ this.buckets.set(key, { tokens, lastRefill: state.lastRefill });
202
+ }
203
+ // --- Cleanup --------------------------------------------------------------
204
+ /**
205
+ * Drops stale keys and trims sliding-window timestamps.
206
+ * Runs on the background interval and can be invoked after mutations if needed.
207
+ */
208
+ purgeExpired() {
209
+ const now = Date.now();
210
+ switch (this.strategy) {
211
+ case RateLimitStrategy.SLIDING_WINDOW:
212
+ this.purgeSliding(now);
213
+ break;
214
+ case RateLimitStrategy.FIXED_WINDOW:
215
+ this.purgeFixed(now);
216
+ break;
217
+ case RateLimitStrategy.TOKEN_BUCKET:
218
+ this.purgeBuckets(now);
219
+ break;
220
+ default:
221
+ break;
222
+ }
223
+ }
224
+ purgeSliding(now) {
225
+ const cutoff = now - this.windowMs;
226
+ for (const [k, ts] of this.sliding.entries()) {
227
+ const filtered = ts.filter((t) => t > cutoff);
228
+ if (filtered.length === 0) {
229
+ this.sliding.delete(k);
230
+ }
231
+ else if (filtered.length !== ts.length) {
232
+ this.sliding.set(k, filtered);
233
+ }
234
+ }
235
+ }
236
+ purgeFixed(now) {
237
+ for (const [k, v] of this.fixed.entries()) {
238
+ if (now >= v.resetTime) {
239
+ this.fixed.delete(k);
240
+ }
241
+ }
242
+ }
243
+ /**
244
+ * Remove idle full buckets to cap memory (activity is tracked via `lastRefill`).
245
+ */
246
+ purgeBuckets(now) {
247
+ const idleMs = 10 * this.refillIntervalMs;
248
+ for (const [k, v] of this.buckets.entries()) {
249
+ if (v.tokens >= this.bucketSize && now - v.lastRefill > idleMs) {
250
+ this.buckets.delete(k);
251
+ }
252
+ }
253
+ }
254
+ }
255
+ //# sourceMappingURL=memory-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../src/stores/memory-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import type { RateLimitResult, RateLimitStore } from '../types/index.js';
2
+ import { RateLimitStrategy } from '../types/index.js';
3
+ /** Window-based strategies (sliding / fixed). */
4
+ export type RedisStoreWindowOptions = {
5
+ strategy: RateLimitStrategy.SLIDING_WINDOW | RateLimitStrategy.FIXED_WINDOW;
6
+ windowMs: number;
7
+ maxRequests: number;
8
+ };
9
+ /** Token-bucket strategy. */
10
+ export type RedisStoreTokenBucketOptions = {
11
+ strategy: RateLimitStrategy.TOKEN_BUCKET;
12
+ tokensPerInterval: number;
13
+ interval: number;
14
+ bucketSize: number;
15
+ };
16
+ export type RedisStoreStrategyOptions = RedisStoreWindowOptions | RedisStoreTokenBucketOptions;
17
+ /**
18
+ * Minimal Redis surface used by {@link RedisStore}.
19
+ * Matches common `EVAL` calling conventions (e.g. ioredis: `eval(script, numKeys, ...keys, ...args)`).
20
+ */
21
+ export interface RedisLikeClient {
22
+ get(key: string): Promise<string | null | undefined>;
23
+ set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
24
+ /**
25
+ * `EVAL` / `EVALSHA` compatible entry point (ioredis-style):
26
+ * `eval(script, numKeys, key1, key2, ..., arg1, arg2, ...)`.
27
+ */
28
+ eval(script: string, numKeys: number, ...keysAndArgs: string[]): Promise<unknown>;
29
+ del?: (...keys: string[]) => Promise<unknown>;
30
+ quit?: () => Promise<unknown>;
31
+ disconnect?: () => void | Promise<void>;
32
+ }
33
+ export type RedisStoreOptions = RedisStoreStrategyOptions & {
34
+ /** Existing Redis client — use {@link adaptIoRedisClient} / {@link adaptNodeRedisClient} if needed. */
35
+ client?: RedisLikeClient;
36
+ /**
37
+ * Connection URL. Requires optional peer dependency `ioredis` (dynamic import).
38
+ * Do not pass both `url` and `client`.
39
+ */
40
+ url?: string;
41
+ /** Prefix for all keys. @default "rlf:" */
42
+ keyPrefix?: string;
43
+ /**
44
+ * Invoked on Redis errors (connection, eval, etc.). Defaults to `console.warn`.
45
+ * Errors are swallowed after warning so your HTTP server keeps running.
46
+ */
47
+ onWarn?: (message: string, error?: unknown) => void;
48
+ };
49
+ /**
50
+ * Wrap an **ioredis** client instance to satisfy {@link RedisLikeClient} without adding a compile-time dependency.
51
+ */
52
+ export declare function adaptIoRedisClient(client: {
53
+ get(key: string): Promise<string | null>;
54
+ set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
55
+ eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
56
+ del?: (...keys: string[]) => Promise<unknown>;
57
+ quit?: () => Promise<unknown>;
58
+ disconnect?: () => void | Promise<void>;
59
+ }): RedisLikeClient;
60
+ /**
61
+ * Wrap **node-redis** v4+ clients (`eval(script, { keys, arguments })`).
62
+ * Does not add a `redis` package dependency — pass your connected client instance.
63
+ */
64
+ export declare function adaptNodeRedisClient(client: {
65
+ get(key: string): Promise<string | null | undefined>;
66
+ set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
67
+ eval(script: string, options: {
68
+ keys: string[];
69
+ arguments: string[];
70
+ }): Promise<unknown>;
71
+ del?: (...keys: string[]) => Promise<unknown>;
72
+ quit?: () => Promise<unknown>;
73
+ disconnect?: () => void | Promise<void>;
74
+ }): RedisLikeClient;
75
+ /**
76
+ * Redis-backed {@link RateLimitStore} using Lua for atomicity.
77
+ *
78
+ * Pass either `client` (recommended) or `url` (loads optional peer `ioredis` at runtime).
79
+ */
80
+ export declare class RedisStore implements RateLimitStore {
81
+ private readonly strategy;
82
+ private readonly windowMs;
83
+ private readonly maxRequests;
84
+ private readonly tokensPerInterval;
85
+ private readonly refillIntervalMs;
86
+ private readonly bucketSize;
87
+ private readonly keyPrefix;
88
+ private readonly onWarn;
89
+ private client;
90
+ private readonly clientPromise;
91
+ /** Connection created from `url` — closed on {@link RedisStore.shutdown}. */
92
+ private ownedRedis;
93
+ constructor(options: RedisStoreOptions);
94
+ private connectFromUrl;
95
+ private warn;
96
+ private getClient;
97
+ private redisKey;
98
+ private evalScript;
99
+ private delKeys;
100
+ private failOpenResult;
101
+ /** @inheritdoc */
102
+ increment(key: string): Promise<RateLimitResult>;
103
+ private incrSliding;
104
+ private incrFixed;
105
+ private incrBucket;
106
+ /** @inheritdoc */
107
+ decrement(key: string): Promise<void>;
108
+ /** @inheritdoc */
109
+ reset(key: string): Promise<void>;
110
+ /** @inheritdoc */
111
+ shutdown(): Promise<void>;
112
+ }
113
+ //# sourceMappingURL=redis-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-store.d.ts","sourceRoot":"","sources":["../../src/stores/redis-store.ts"],"names":[],"mappings":"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"}