@stimulcross/rate-limiter 0.0.1 → 0.0.3

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 (238) hide show
  1. package/README.md +20 -0
  2. package/lib/core/cancellable.d.ts +5 -0
  3. package/lib/core/cancellable.js +2 -0
  4. package/lib/core/clock.d.ts +10 -0
  5. package/lib/core/clock.js +2 -0
  6. package/{src/core/decision.ts → lib/core/decision.d.ts} +7 -11
  7. package/lib/core/decision.js +2 -0
  8. package/lib/core/rate-limit-policy.d.ts +14 -0
  9. package/lib/core/rate-limit-policy.js +2 -0
  10. package/lib/core/rate-limiter-status.d.ts +14 -0
  11. package/lib/core/rate-limiter-status.js +2 -0
  12. package/lib/core/rate-limiter.d.ts +34 -0
  13. package/lib/core/rate-limiter.js +2 -0
  14. package/lib/core/state-storage.d.ts +46 -0
  15. package/lib/core/state-storage.js +2 -0
  16. package/lib/enums/rate-limit-error-code.d.ts +26 -0
  17. package/lib/enums/rate-limit-error-code.js +27 -0
  18. package/lib/errors/custom.error.d.ts +6 -0
  19. package/lib/errors/custom.error.js +13 -0
  20. package/lib/errors/invalid-cost.error.d.ts +16 -0
  21. package/lib/errors/invalid-cost.error.js +26 -0
  22. package/lib/errors/rate-limit.error.d.ts +37 -0
  23. package/lib/errors/rate-limit.error.js +75 -0
  24. package/lib/errors/rate-limiter-destroyed.error.d.ts +7 -0
  25. package/lib/errors/rate-limiter-destroyed.error.js +9 -0
  26. package/{src/index.ts → lib/index.d.ts} +1 -0
  27. package/lib/index.js +5 -0
  28. package/lib/interfaces/rate-limiter-options.d.ts +76 -0
  29. package/lib/interfaces/rate-limiter-options.js +2 -0
  30. package/lib/interfaces/rate-limiter-queue-options.d.ts +42 -0
  31. package/lib/interfaces/rate-limiter-queue-options.js +2 -0
  32. package/lib/interfaces/rate-limiter-run-options.d.ts +52 -0
  33. package/lib/interfaces/rate-limiter-run-options.js +2 -0
  34. package/lib/limiters/abstract-rate-limiter.d.ts +44 -0
  35. package/lib/limiters/abstract-rate-limiter.js +133 -0
  36. package/lib/limiters/composite.policy.d.ts +15 -0
  37. package/lib/limiters/composite.policy.js +73 -0
  38. package/lib/limiters/fixed-window/fixed-window.limiter.d.ts +33 -0
  39. package/lib/limiters/fixed-window/fixed-window.limiter.js +85 -0
  40. package/lib/limiters/fixed-window/fixed-window.options.d.ts +27 -0
  41. package/lib/limiters/fixed-window/fixed-window.options.js +2 -0
  42. package/lib/limiters/fixed-window/fixed-window.policy.d.ts +19 -0
  43. package/lib/limiters/fixed-window/fixed-window.policy.js +121 -0
  44. package/{src/limiters/fixed-window/fixed-window.state.ts → lib/limiters/fixed-window/fixed-window.state.d.ts} +4 -3
  45. package/lib/limiters/fixed-window/fixed-window.state.js +2 -0
  46. package/lib/limiters/fixed-window/fixed-window.status.d.ts +39 -0
  47. package/lib/limiters/fixed-window/fixed-window.status.js +2 -0
  48. package/{src/limiters/fixed-window/index.ts → lib/limiters/fixed-window/index.d.ts} +1 -0
  49. package/lib/limiters/fixed-window/index.js +2 -0
  50. package/lib/limiters/generic-cell/generic-cell.limiter.d.ts +30 -0
  51. package/lib/limiters/generic-cell/generic-cell.limiter.js +74 -0
  52. package/lib/limiters/generic-cell/generic-cell.options.d.ts +22 -0
  53. package/lib/limiters/generic-cell/generic-cell.options.js +2 -0
  54. package/lib/limiters/generic-cell/generic-cell.policy.d.ts +18 -0
  55. package/lib/limiters/generic-cell/generic-cell.policy.js +87 -0
  56. package/{src/limiters/generic-cell/generic-cell.state.ts → lib/limiters/generic-cell/generic-cell.state.d.ts} +2 -1
  57. package/lib/limiters/generic-cell/generic-cell.state.js +2 -0
  58. package/lib/limiters/generic-cell/generic-cell.status.d.ts +49 -0
  59. package/lib/limiters/generic-cell/generic-cell.status.js +2 -0
  60. package/{src/limiters/generic-cell/index.ts → lib/limiters/generic-cell/index.d.ts} +1 -0
  61. package/lib/limiters/generic-cell/index.js +2 -0
  62. package/{src/limiters/http-response-based/http-limit-info.extractor.ts → lib/limiters/http-response-based/http-limit-info.extractor.d.ts} +2 -6
  63. package/lib/limiters/http-response-based/http-limit-info.extractor.js +2 -0
  64. package/lib/limiters/http-response-based/http-limit.info.d.ts +39 -0
  65. package/lib/limiters/http-response-based/http-limit.info.js +2 -0
  66. package/{src/limiters/http-response-based/http-response-based-limiter.options.ts → lib/limiters/http-response-based/http-response-based-limiter.options.d.ts} +9 -10
  67. package/lib/limiters/http-response-based/http-response-based-limiter.options.js +2 -0
  68. package/lib/limiters/http-response-based/http-response-based-limiter.state.d.ts +14 -0
  69. package/lib/limiters/http-response-based/http-response-based-limiter.state.js +2 -0
  70. package/lib/limiters/http-response-based/http-response-based-limiter.status.d.ts +70 -0
  71. package/lib/limiters/http-response-based/http-response-based-limiter.status.js +2 -0
  72. package/lib/limiters/http-response-based/http-response-based.limiter.d.ts +56 -0
  73. package/lib/limiters/http-response-based/http-response-based.limiter.js +386 -0
  74. package/{src/limiters/http-response-based/index.ts → lib/limiters/http-response-based/index.d.ts} +1 -0
  75. package/lib/limiters/http-response-based/index.js +2 -0
  76. package/{src/limiters/leaky-bucket/index.ts → lib/limiters/leaky-bucket/index.d.ts} +1 -0
  77. package/lib/limiters/leaky-bucket/index.js +2 -0
  78. package/lib/limiters/leaky-bucket/leaky-bucket.limiter.d.ts +30 -0
  79. package/lib/limiters/leaky-bucket/leaky-bucket.limiter.js +75 -0
  80. package/lib/limiters/leaky-bucket/leaky-bucket.options.d.ts +22 -0
  81. package/lib/limiters/leaky-bucket/leaky-bucket.options.js +2 -0
  82. package/lib/limiters/leaky-bucket/leaky-bucket.policy.d.ts +19 -0
  83. package/lib/limiters/leaky-bucket/leaky-bucket.policy.js +101 -0
  84. package/{src/limiters/leaky-bucket/leaky-bucket.state.ts → lib/limiters/leaky-bucket/leaky-bucket.state.d.ts} +3 -2
  85. package/lib/limiters/leaky-bucket/leaky-bucket.state.js +2 -0
  86. package/lib/limiters/leaky-bucket/leaky-bucket.status.d.ts +31 -0
  87. package/lib/limiters/leaky-bucket/leaky-bucket.status.js +2 -0
  88. package/{src/limiters/sliding-window-counter/index.ts → lib/limiters/sliding-window-counter/index.d.ts} +2 -4
  89. package/lib/limiters/sliding-window-counter/index.js +2 -0
  90. package/lib/limiters/sliding-window-counter/sliding-window-counter.limiter.d.ts +28 -0
  91. package/lib/limiters/sliding-window-counter/sliding-window-counter.limiter.js +47 -0
  92. package/lib/limiters/sliding-window-counter/sliding-window-counter.options.d.ts +16 -0
  93. package/lib/limiters/sliding-window-counter/sliding-window-counter.options.js +2 -0
  94. package/lib/limiters/sliding-window-counter/sliding-window-counter.policy.d.ts +18 -0
  95. package/lib/limiters/sliding-window-counter/sliding-window-counter.policy.js +128 -0
  96. package/{src/limiters/sliding-window-counter/sliding-window-counter.state.ts → lib/limiters/sliding-window-counter/sliding-window-counter.state.d.ts} +4 -3
  97. package/lib/limiters/sliding-window-counter/sliding-window-counter.state.js +2 -0
  98. package/lib/limiters/sliding-window-counter/sliding-window-counter.status.d.ts +45 -0
  99. package/lib/limiters/sliding-window-counter/sliding-window-counter.status.js +2 -0
  100. package/{src/limiters/sliding-window-log/index.ts → lib/limiters/sliding-window-log/index.d.ts} +1 -0
  101. package/lib/limiters/sliding-window-log/index.js +2 -0
  102. package/lib/limiters/sliding-window-log/sliding-window-log.limiter.d.ts +27 -0
  103. package/lib/limiters/sliding-window-log/sliding-window-log.limiter.js +44 -0
  104. package/lib/limiters/sliding-window-log/sliding-window-log.options.d.ts +16 -0
  105. package/lib/limiters/sliding-window-log/sliding-window-log.options.js +2 -0
  106. package/lib/limiters/sliding-window-log/sliding-window-log.policy.d.ts +18 -0
  107. package/lib/limiters/sliding-window-log/sliding-window-log.policy.js +124 -0
  108. package/{src/limiters/sliding-window-log/sliding-window-log.state.ts → lib/limiters/sliding-window-log/sliding-window-log.state.d.ts} +5 -6
  109. package/lib/limiters/sliding-window-log/sliding-window-log.state.js +2 -0
  110. package/lib/limiters/sliding-window-log/sliding-window-log.status.d.ts +39 -0
  111. package/lib/limiters/sliding-window-log/sliding-window-log.status.js +2 -0
  112. package/{src/limiters/token-bucket/index.ts → lib/limiters/token-bucket/index.d.ts} +1 -0
  113. package/lib/limiters/token-bucket/index.js +2 -0
  114. package/lib/limiters/token-bucket/token-bucket.limiter.d.ts +30 -0
  115. package/lib/limiters/token-bucket/token-bucket.limiter.js +75 -0
  116. package/{src/limiters/token-bucket/token-bucket.options.ts → lib/limiters/token-bucket/token-bucket.options.d.ts} +9 -10
  117. package/lib/limiters/token-bucket/token-bucket.options.js +2 -0
  118. package/lib/limiters/token-bucket/token-bucket.policy.d.ts +19 -0
  119. package/lib/limiters/token-bucket/token-bucket.policy.js +116 -0
  120. package/{src/limiters/token-bucket/token-bucket.state.ts → lib/limiters/token-bucket/token-bucket.state.d.ts} +4 -3
  121. package/lib/limiters/token-bucket/token-bucket.state.js +2 -0
  122. package/lib/limiters/token-bucket/token-bucket.status.d.ts +31 -0
  123. package/lib/limiters/token-bucket/token-bucket.status.js +2 -0
  124. package/lib/runtime/default-clock.d.ts +4 -0
  125. package/lib/runtime/default-clock.js +7 -0
  126. package/lib/runtime/execution-tickets.d.ts +12 -0
  127. package/lib/runtime/execution-tickets.js +27 -0
  128. package/lib/runtime/in-memory-state-store.d.ts +19 -0
  129. package/lib/runtime/in-memory-state-store.js +97 -0
  130. package/lib/runtime/rate-limiter.executor.d.ts +47 -0
  131. package/lib/runtime/rate-limiter.executor.js +196 -0
  132. package/lib/runtime/semaphore.d.ts +9 -0
  133. package/lib/runtime/semaphore.js +28 -0
  134. package/lib/runtime/task.d.ts +41 -0
  135. package/lib/runtime/task.js +101 -0
  136. package/{src/types/limit-behavior.ts → lib/types/limit-behavior.d.ts} +1 -0
  137. package/lib/types/limit-behavior.js +2 -0
  138. package/lib/utils/generate-random-string.d.ts +3 -0
  139. package/lib/utils/generate-random-string.js +13 -0
  140. package/lib/utils/promise-with-resolvers.d.ts +9 -0
  141. package/lib/utils/promise-with-resolvers.js +15 -0
  142. package/lib/utils/sanitize-error.d.ts +3 -0
  143. package/lib/utils/sanitize-error.js +5 -0
  144. package/lib/utils/sanitize-priority.d.ts +4 -0
  145. package/lib/utils/sanitize-priority.js +18 -0
  146. package/lib/utils/validate-cost.d.ts +3 -0
  147. package/lib/utils/validate-cost.js +14 -0
  148. package/package.json +13 -2
  149. package/.editorconfig +0 -21
  150. package/.github/workflows/node.yml +0 -87
  151. package/.husky/commit-msg +0 -1
  152. package/.husky/pre-commit +0 -1
  153. package/.megaignore +0 -8
  154. package/.prettierignore +0 -3
  155. package/commitlint.config.js +0 -8
  156. package/eslint.config.js +0 -65
  157. package/lint-staged.config.js +0 -4
  158. package/prettier.config.cjs +0 -1
  159. package/src/core/cancellable.ts +0 -4
  160. package/src/core/clock.ts +0 -9
  161. package/src/core/rate-limit-policy.ts +0 -15
  162. package/src/core/rate-limiter-status.ts +0 -14
  163. package/src/core/rate-limiter.ts +0 -37
  164. package/src/core/state-storage.ts +0 -51
  165. package/src/enums/rate-limit-error-code.ts +0 -29
  166. package/src/errors/custom.error.ts +0 -14
  167. package/src/errors/invalid-cost.error.ts +0 -33
  168. package/src/errors/rate-limit.error.ts +0 -91
  169. package/src/errors/rate-limiter-destroyed.error.ts +0 -8
  170. package/src/interfaces/rate-limiter-options.ts +0 -84
  171. package/src/interfaces/rate-limiter-queue-options.ts +0 -45
  172. package/src/interfaces/rate-limiter-run-options.ts +0 -58
  173. package/src/limiters/abstract-rate-limiter.ts +0 -206
  174. package/src/limiters/composite.policy.ts +0 -102
  175. package/src/limiters/fixed-window/fixed-window.limiter.ts +0 -121
  176. package/src/limiters/fixed-window/fixed-window.options.ts +0 -29
  177. package/src/limiters/fixed-window/fixed-window.policy.ts +0 -159
  178. package/src/limiters/fixed-window/fixed-window.status.ts +0 -46
  179. package/src/limiters/generic-cell/generic-cell.limiter.ts +0 -108
  180. package/src/limiters/generic-cell/generic-cell.options.ts +0 -23
  181. package/src/limiters/generic-cell/generic-cell.policy.ts +0 -115
  182. package/src/limiters/generic-cell/generic-cell.status.ts +0 -54
  183. package/src/limiters/http-response-based/http-limit.info.ts +0 -41
  184. package/src/limiters/http-response-based/http-response-based-limiter.state.ts +0 -13
  185. package/src/limiters/http-response-based/http-response-based-limiter.status.ts +0 -74
  186. package/src/limiters/http-response-based/http-response-based.limiter.ts +0 -512
  187. package/src/limiters/leaky-bucket/leaky-bucket.limiter.ts +0 -105
  188. package/src/limiters/leaky-bucket/leaky-bucket.options.ts +0 -23
  189. package/src/limiters/leaky-bucket/leaky-bucket.policy.ts +0 -134
  190. package/src/limiters/leaky-bucket/leaky-bucket.status.ts +0 -36
  191. package/src/limiters/sliding-window-counter/sliding-window-counter.limiter.ts +0 -76
  192. package/src/limiters/sliding-window-counter/sliding-window-counter.options.ts +0 -20
  193. package/src/limiters/sliding-window-counter/sliding-window-counter.policy.ts +0 -167
  194. package/src/limiters/sliding-window-counter/sliding-window-counter.status.ts +0 -53
  195. package/src/limiters/sliding-window-log/sliding-window-log.limiter.ts +0 -65
  196. package/src/limiters/sliding-window-log/sliding-window-log.options.ts +0 -20
  197. package/src/limiters/sliding-window-log/sliding-window-log.policy.ts +0 -166
  198. package/src/limiters/sliding-window-log/sliding-window-log.status.ts +0 -44
  199. package/src/limiters/token-bucket/token-bucket.limiter.ts +0 -110
  200. package/src/limiters/token-bucket/token-bucket.policy.ts +0 -155
  201. package/src/limiters/token-bucket/token-bucket.status.ts +0 -36
  202. package/src/runtime/default-clock.ts +0 -8
  203. package/src/runtime/execution-tickets.ts +0 -34
  204. package/src/runtime/in-memory-state-store.ts +0 -135
  205. package/src/runtime/rate-limiter.executor.ts +0 -286
  206. package/src/runtime/semaphore.ts +0 -31
  207. package/src/runtime/task.ts +0 -141
  208. package/src/utils/generate-random-string.ts +0 -16
  209. package/src/utils/promise-with-resolvers.ts +0 -23
  210. package/src/utils/sanitize-error.ts +0 -4
  211. package/src/utils/sanitize-priority.ts +0 -22
  212. package/src/utils/validate-cost.ts +0 -16
  213. package/tests/integration/limiters/fixed-window.limiter.spec.ts +0 -371
  214. package/tests/integration/limiters/generic-cell.limiter.spec.ts +0 -361
  215. package/tests/integration/limiters/http-response-based.limiter.spec.ts +0 -833
  216. package/tests/integration/limiters/leaky-bucket.spec.ts +0 -357
  217. package/tests/integration/limiters/sliding-window-counter.limiter.spec.ts +0 -175
  218. package/tests/integration/limiters/sliding-window-log.spec.ts +0 -185
  219. package/tests/integration/limiters/token-bucket.limiter.spec.ts +0 -363
  220. package/tests/tsconfig.json +0 -4
  221. package/tests/unit/policies/composite.policy.spec.ts +0 -244
  222. package/tests/unit/policies/fixed-window.policy.spec.ts +0 -260
  223. package/tests/unit/policies/generic-cell.policy.spec.ts +0 -178
  224. package/tests/unit/policies/leaky-bucket.policy.spec.ts +0 -215
  225. package/tests/unit/policies/sliding-window-counter.policy.spec.ts +0 -209
  226. package/tests/unit/policies/sliding-window-log.policy.spec.ts +0 -285
  227. package/tests/unit/policies/token-bucket.policy.spec.ts +0 -371
  228. package/tests/unit/runtime/execution-tickets.spec.ts +0 -121
  229. package/tests/unit/runtime/in-memory-state-store.spec.ts +0 -238
  230. package/tests/unit/runtime/rate-limiter.executor.spec.ts +0 -353
  231. package/tests/unit/runtime/semaphore.spec.ts +0 -98
  232. package/tests/unit/runtime/task.spec.ts +0 -182
  233. package/tests/unit/utils/generate-random-string.spec.ts +0 -51
  234. package/tests/unit/utils/promise-with-resolvers.spec.ts +0 -57
  235. package/tests/unit/utils/sanitize-priority.spec.ts +0 -46
  236. package/tests/unit/utils/validate-cost.spec.ts +0 -48
  237. package/tsconfig.json +0 -14
  238. package/vitest.config.js +0 -22
package/README.md CHANGED
@@ -5,3 +5,23 @@ A collection of rate limiters designed primarily for **outbound request throttli
5
5
  They are suited for client-side usage to respect third-party limits or protect internal resources.
6
6
 
7
7
  While it is technically possible to use these limiters for server-side traffic backed by a distributed store like Redis, it is **not recommended**. The algorithms evaluate state within the application process, so distributed usage requires multiple network operations per request introducing significant round-trip latency.
8
+
9
+ ## Installation
10
+
11
+ **npm**
12
+
13
+ ```
14
+ npm add @stimulcross/rate-limiter
15
+ ```
16
+
17
+ **pnpm**
18
+
19
+ ```
20
+ pnpm add @stimulcross/rate-limiter
21
+ ```
22
+
23
+ **yarn**
24
+
25
+ ```
26
+ yarn add @stimulcross/rate-limiter
27
+ ```
@@ -0,0 +1,5 @@
1
+ /** @internal */
2
+ export interface Cancellable {
3
+ cancel(): void;
4
+ }
5
+ //# sourceMappingURL=cancellable.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cancellable.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Clock interface.
3
+ */
4
+ export interface Clock {
5
+ /**
6
+ * Returns the current timestamp in milliseconds.
7
+ */
8
+ now(): number;
9
+ }
10
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=clock.js.map
@@ -1,27 +1,23 @@
1
1
  /** @internal */
2
2
  export type DecisionKind = 'allow' | 'deny' | 'delay';
3
-
4
3
  /** @internal */
5
4
  export interface DecisionBase {
6
- kind: DecisionKind;
5
+ kind: DecisionKind;
7
6
  }
8
-
9
7
  /** @internal */
10
8
  export interface DecisionAllow extends DecisionBase {
11
- kind: Extract<DecisionKind, 'allow'>;
9
+ kind: Extract<DecisionKind, 'allow'>;
12
10
  }
13
-
14
11
  /** @internal */
15
12
  export interface DecisionDeny extends DecisionBase {
16
- kind: Extract<DecisionKind, 'deny'>;
17
- retryAt: number;
13
+ kind: Extract<DecisionKind, 'deny'>;
14
+ retryAt: number;
18
15
  }
19
-
20
16
  /** @internal */
21
17
  export interface DecisionDelay extends DecisionBase {
22
- kind: Extract<DecisionKind, 'delay'>;
23
- runAt: number;
18
+ kind: Extract<DecisionKind, 'delay'>;
19
+ runAt: number;
24
20
  }
25
-
26
21
  /** @internal */
27
22
  export type Decision = DecisionAllow | DecisionDeny | DecisionDelay;
23
+ //# sourceMappingURL=decision.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=decision.js.map
@@ -0,0 +1,14 @@
1
+ import { type Decision } from './decision.js';
2
+ /** @internal */
3
+ export interface RateLimitPolicyResult<S> {
4
+ decision: Decision;
5
+ nextState: S;
6
+ }
7
+ /** @internal */
8
+ export interface RateLimitPolicy<TState extends object = object, TStatus extends object = object> {
9
+ getInitialState(now: number): TState;
10
+ getStatus(state: TState, now: number): TStatus;
11
+ evaluate(state: TState, now: number, cost: number, shouldReserve?: boolean): RateLimitPolicyResult<TState>;
12
+ revert(state: TState, cost: number, now: number): TState;
13
+ }
14
+ //# sourceMappingURL=rate-limit-policy.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limit-policy.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The status of the rate limiter.
3
+ */
4
+ export interface RateLimiterStatus {
5
+ /**
6
+ * The timestamp (in milliseconds) when a rate limiter will allow a single request.
7
+ */
8
+ readonly nextAvailableAt: number;
9
+ /**
10
+ * The timestamp (in milliseconds) when the rate limiter will reset.
11
+ */
12
+ readonly resetAt: number;
13
+ }
14
+ //# sourceMappingURL=rate-limiter-status.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limiter-status.js.map
@@ -0,0 +1,34 @@
1
+ import { type RateLimiterRunOptions } from '../interfaces/rate-limiter-run-options.js';
2
+ /**
3
+ * Rate limiter interface.
4
+ *
5
+ * @template TStatus The type of the rate limiter status returned by {@link getStatus} method.
6
+ */
7
+ export interface RateLimiter<TStatus extends object = object> {
8
+ /**
9
+ * Runs the given task.
10
+ *
11
+ * @param task The task to run.
12
+ * @param options Options for running the task.
13
+ */
14
+ run<T>(task: () => T | Promise<T>, options?: RateLimiterRunOptions): Promise<T>;
15
+ /**
16
+ * Clears the rate limiter state.
17
+ *
18
+ * @param key The optional key to clear the state for.
19
+ */
20
+ clear(key?: string): Promise<void>;
21
+ /**
22
+ * Gets the rate limiter's status.
23
+ *
24
+ * @param key The optional key to get the status for.
25
+ */
26
+ getStatus?(key?: string): Promise<TStatus>;
27
+ /**
28
+ * Destroys the rate limiter.
29
+ *
30
+ * The limiter cannot be used after it has been destroyed. It should be used only for graceful shutdown.
31
+ */
32
+ destroy?(): Promise<void>;
33
+ }
34
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * State storage interface.
3
+ */
4
+ export interface StateStorage<TState> {
5
+ /**
6
+ * Gets the state for the given key.
7
+ *
8
+ * @param key The key to get the state for.
9
+ */
10
+ get(key: string): Promise<TState | null>;
11
+ /**
12
+ * Sets the state for the given key.
13
+ *
14
+ * @param key The key to set the state for.
15
+ * @param value The state to set.
16
+ * @param ttlMs Optional TTL in milliseconds.
17
+ */
18
+ set(key: string, value: TState, ttlMs?: number): Promise<void>;
19
+ /**
20
+ * Deletes the state for the given key.
21
+ *
22
+ * @param key The key to delete the state for.
23
+ */
24
+ delete(key: string): Promise<void>;
25
+ /**
26
+ * Clears all stored states.
27
+ */
28
+ clear(): Promise<void>;
29
+ /**
30
+ * Destroys the storage.
31
+ */
32
+ destroy?(): Promise<void>;
33
+ /**
34
+ * Acquires a lock for the given key.
35
+ *
36
+ * @param key The key to acquire the lock for.
37
+ */
38
+ acquireLock?(key: string): Promise<void>;
39
+ /**
40
+ * Releases the lock for the given key.
41
+ *
42
+ * @param key The key to release the lock for.
43
+ */
44
+ releaseLock?(key: string): Promise<void>;
45
+ }
46
+ //# sourceMappingURL=state-storage.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state-storage.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Rate limiter error codes.
3
+ */
4
+ export declare enum RateLimitErrorCode {
5
+ /**
6
+ * Indicates that the limit has been reached.
7
+ */
8
+ LimitExceeded = "LIMIT_EXCEEDED",
9
+ /**
10
+ * Indicates that the execution queue is full.
11
+ */
12
+ QueueOverflow = "QUEUE_OVERFLOW",
13
+ /**
14
+ * Indicates that the task has expired.
15
+ */
16
+ Expired = "EXPIRED",
17
+ /**
18
+ * Indicates that a task was cleared before it was executed.
19
+ */
20
+ Destroyed = "DESTROYED",
21
+ /**
22
+ * Indicates that a task was canceled via abort controller before it was executed.
23
+ */
24
+ Cancelled = "CANCELLED"
25
+ }
26
+ //# sourceMappingURL=rate-limit-error-code.d.ts.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Rate limiter error codes.
3
+ */
4
+ export var RateLimitErrorCode;
5
+ (function (RateLimitErrorCode) {
6
+ /**
7
+ * Indicates that the limit has been reached.
8
+ */
9
+ RateLimitErrorCode["LimitExceeded"] = "LIMIT_EXCEEDED";
10
+ /**
11
+ * Indicates that the execution queue is full.
12
+ */
13
+ RateLimitErrorCode["QueueOverflow"] = "QUEUE_OVERFLOW";
14
+ /**
15
+ * Indicates that the task has expired.
16
+ */
17
+ RateLimitErrorCode["Expired"] = "EXPIRED";
18
+ /**
19
+ * Indicates that a task was cleared before it was executed.
20
+ */
21
+ RateLimitErrorCode["Destroyed"] = "DESTROYED";
22
+ /**
23
+ * Indicates that a task was canceled via abort controller before it was executed.
24
+ */
25
+ RateLimitErrorCode["Cancelled"] = "CANCELLED";
26
+ })(RateLimitErrorCode || (RateLimitErrorCode = {}));
27
+ //# sourceMappingURL=rate-limit-error-code.js.map
@@ -0,0 +1,6 @@
1
+ /** @internal */
2
+ export declare abstract class CustomError extends Error {
3
+ protected constructor(message: string);
4
+ get name(): string;
5
+ }
6
+ //# sourceMappingURL=custom.error.d.ts.map
@@ -0,0 +1,13 @@
1
+ /** @internal */
2
+ export class CustomError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ Object.setPrototypeOf(this, new.target.prototype);
6
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
7
+ Error.captureStackTrace?.(this, new.target.constructor);
8
+ }
9
+ get name() {
10
+ return this.constructor.name;
11
+ }
12
+ }
13
+ //# sourceMappingURL=custom.error.js.map
@@ -0,0 +1,16 @@
1
+ import { CustomError } from './custom.error.js';
2
+ export interface InvalidCostErrorPlainObject extends Error {
3
+ cost: number;
4
+ }
5
+ /**
6
+ * Error thrown when the cost is invalid.
7
+ *
8
+ * The cost must be a positive integer or zero.
9
+ */
10
+ export declare class InvalidCostError extends CustomError {
11
+ private readonly _cost;
12
+ constructor(message: string, _cost: number);
13
+ get cost(): number;
14
+ toJSON(): InvalidCostErrorPlainObject;
15
+ }
16
+ //# sourceMappingURL=invalid-cost.error.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { CustomError } from './custom.error.js';
2
+ /**
3
+ * Error thrown when the cost is invalid.
4
+ *
5
+ * The cost must be a positive integer or zero.
6
+ */
7
+ export class InvalidCostError extends CustomError {
8
+ _cost;
9
+ constructor(message, _cost) {
10
+ super(message);
11
+ this._cost = _cost;
12
+ }
13
+ get cost() {
14
+ return this._cost;
15
+ }
16
+ // eslint-disable-next-line @typescript-eslint/naming-convention
17
+ toJSON() {
18
+ return {
19
+ name: this.name,
20
+ message: this.message,
21
+ cost: this._cost,
22
+ stack: this.stack,
23
+ };
24
+ }
25
+ }
26
+ //# sourceMappingURL=invalid-cost.error.js.map
@@ -0,0 +1,37 @@
1
+ import { RateLimitErrorCode } from '../enums/rate-limit-error-code.js';
2
+ export interface RateLimitErrorPlainObject extends Error {
3
+ code: RateLimitErrorCode;
4
+ retryAt: number | null;
5
+ }
6
+ /**
7
+ * An error thrown when a rate limit is exceeded.
8
+ *
9
+ * This error has a {@link code} property that indicates the type of error.
10
+ *
11
+ * The `code` can be:
12
+ * - `LIMIT_EXCEEDED` - When the rate limit is exceeded.
13
+ * - `QUEUE_OVERFLOW` - When the queue is full (if the limiter has a queue and the capacity has been exceeded).
14
+ * - `EXPIRED` - When the task has expired (waited too long in the queue). This is related to the `maxWaitMs` option.
15
+ * **NOTE:** This is never thrown if the task is executing too long. Such scenarios should be handled by the
16
+ * task itself.
17
+ * - `DESTROYED` - When the task is destroyed due to the rate limiter's `clear()` or `destroy()` methods.
18
+ * - `CANCELLED` - When the task is cancelled using an abort signal.
19
+ */
20
+ export declare class RateLimitError extends Error {
21
+ private readonly _code;
22
+ private readonly _retryAt;
23
+ /** @internal */
24
+ constructor(code: RateLimitErrorCode, retryAt?: number, message?: string);
25
+ /**
26
+ * The error code.
27
+ */
28
+ get code(): RateLimitErrorCode;
29
+ /**
30
+ * The timestamp (in milliseconds) when the task can be retried.
31
+ *
32
+ * Can be `null` if the retry time is not known.
33
+ */
34
+ get retryAt(): number | null;
35
+ toJSON(): RateLimitErrorPlainObject;
36
+ }
37
+ //# sourceMappingURL=rate-limit.error.d.ts.map
@@ -0,0 +1,75 @@
1
+ import { RateLimitErrorCode } from '../enums/rate-limit-error-code.js';
2
+ /**
3
+ * An error thrown when a rate limit is exceeded.
4
+ *
5
+ * This error has a {@link code} property that indicates the type of error.
6
+ *
7
+ * The `code` can be:
8
+ * - `LIMIT_EXCEEDED` - When the rate limit is exceeded.
9
+ * - `QUEUE_OVERFLOW` - When the queue is full (if the limiter has a queue and the capacity has been exceeded).
10
+ * - `EXPIRED` - When the task has expired (waited too long in the queue). This is related to the `maxWaitMs` option.
11
+ * **NOTE:** This is never thrown if the task is executing too long. Such scenarios should be handled by the
12
+ * task itself.
13
+ * - `DESTROYED` - When the task is destroyed due to the rate limiter's `clear()` or `destroy()` methods.
14
+ * - `CANCELLED` - When the task is cancelled using an abort signal.
15
+ */
16
+ export class RateLimitError extends Error {
17
+ _code;
18
+ _retryAt;
19
+ /** @internal */
20
+ constructor(code, retryAt, message) {
21
+ if (!message) {
22
+ switch (code) {
23
+ case RateLimitErrorCode.LimitExceeded: {
24
+ message = `Rate limit exceeded.${retryAt ? ` Retry at ${new Date(retryAt).toISOString()}.` : ''}`;
25
+ break;
26
+ }
27
+ case RateLimitErrorCode.QueueOverflow: {
28
+ message = 'Queue overflow.';
29
+ break;
30
+ }
31
+ case RateLimitErrorCode.Expired: {
32
+ message = 'Task expired.';
33
+ break;
34
+ }
35
+ case RateLimitErrorCode.Destroyed: {
36
+ message = 'Task destroyed.';
37
+ break;
38
+ }
39
+ case RateLimitErrorCode.Cancelled: {
40
+ message = 'Task cancelled.';
41
+ break;
42
+ }
43
+ // No default
44
+ }
45
+ }
46
+ super(message);
47
+ this._code = code;
48
+ this._retryAt = retryAt ?? null;
49
+ }
50
+ /**
51
+ * The error code.
52
+ */
53
+ get code() {
54
+ return this._code;
55
+ }
56
+ /**
57
+ * The timestamp (in milliseconds) when the task can be retried.
58
+ *
59
+ * Can be `null` if the retry time is not known.
60
+ */
61
+ get retryAt() {
62
+ return this._retryAt ?? null;
63
+ }
64
+ // eslint-disable-next-line @typescript-eslint/naming-convention
65
+ toJSON() {
66
+ return {
67
+ name: this.name,
68
+ message: this.message,
69
+ code: this._code,
70
+ retryAt: this._retryAt,
71
+ stack: this.stack,
72
+ };
73
+ }
74
+ }
75
+ //# sourceMappingURL=rate-limit.error.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * An error thrown when running a task on a destroyed rate limiter.
3
+ */
4
+ export declare class RateLimiterDestroyedError extends Error {
5
+ constructor();
6
+ }
7
+ //# sourceMappingURL=rate-limiter-destroyed.error.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * An error thrown when running a task on a destroyed rate limiter.
3
+ */
4
+ export class RateLimiterDestroyedError extends Error {
5
+ constructor() {
6
+ super('Rate limiter has been destroyed and cannot be used.');
7
+ }
8
+ }
9
+ //# sourceMappingURL=rate-limiter-destroyed.error.js.map
@@ -9,3 +9,4 @@ export { type RateLimitErrorPlainObject, RateLimitError } from './errors/rate-li
9
9
  export { RateLimiterDestroyedError } from './errors/rate-limiter-destroyed.error.js';
10
10
  export { type InvalidCostErrorPlainObject, InvalidCostError } from './errors/invalid-cost.error.js';
11
11
  export { RateLimitErrorCode } from './enums/rate-limit-error-code.js';
12
+ //# sourceMappingURL=index.d.ts.map
package/lib/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { RateLimitError } from './errors/rate-limit.error.js';
2
+ export { RateLimiterDestroyedError } from './errors/rate-limiter-destroyed.error.js';
3
+ export { InvalidCostError } from './errors/invalid-cost.error.js';
4
+ export { RateLimitErrorCode } from './enums/rate-limit-error-code.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,76 @@
1
+ import { type LoggerOptions } from '@stimulcross/logger';
2
+ import { type RateLimiterQueueOptions } from './rate-limiter-queue-options.js';
3
+ import { type Clock } from '../core/clock.js';
4
+ import { type StateStorage } from '../core/state-storage.js';
5
+ import { type LimitBehavior } from '../types/limit-behavior.js';
6
+ /**
7
+ * A function that generates a unique ID for the task.
8
+ */
9
+ export type IdGenerator = () => string;
10
+ /**
11
+ * A function that resolves a global key for the given key.
12
+ */
13
+ export type KeyResolver = (key?: string) => string;
14
+ /**
15
+ * Rate limiter options.
16
+ *
17
+ * @template TState The type of the rate limiter state.
18
+ */
19
+ export interface RateLimiterOptions<TState = unknown> {
20
+ /**
21
+ * A custom clock implementation.
22
+ */
23
+ clock?: Clock;
24
+ /**
25
+ * An optional key that can be either a string or a key resolver function.
26
+ *
27
+ * If a string is provided, it will be used as a global prefix for all keys.
28
+ *
29
+ * If a function is provided, it will be called with the provided key and should return a unique global key.
30
+ *
31
+ * Useful for distributed stores.
32
+ *
33
+ * @default limiter
34
+ */
35
+ key?: string | KeyResolver;
36
+ /**
37
+ * A custom ID factory function.
38
+ *
39
+ * It is used to generate unique IDs for each task for logging and debugging purposes.
40
+ */
41
+ idGenerator?: IdGenerator;
42
+ /**
43
+ * State storage implementation for persisting rate limiter state.
44
+ *
45
+ * By default, an in-memory state store is used, which is unique to each process.
46
+ * This is suitable for single-instance applications or when rate limiting doesn't need
47
+ * to be shared across multiple processes.
48
+ *
49
+ * For distributed applications, you can implement a custom state store using Redis, Memcached,
50
+ * or other distributed storage systems. However, be aware that this introduces network latency
51
+ * due to multiple round-trips (typically 3-4 requests with lock acquire/release).
52
+ *
53
+ * For distributed rate limiting, consider using, for example, Redis with Lua scripts.
54
+ * This allows atomic operations and minimizes latency.
55
+ */
56
+ store?: StateStorage<TState>;
57
+ /**
58
+ * Defines the behavior when the limit is reached.
59
+ *
60
+ * Available options:
61
+ * - `reject` - rejects the task with `LIMIT_EXCEEDED` error code
62
+ * - `enqueue` - enqueues the task
63
+ *
64
+ * @default 'reject'
65
+ */
66
+ limitBehavior?: LimitBehavior;
67
+ /**
68
+ * Logger options.
69
+ */
70
+ loggerOptions?: Omit<LoggerOptions, 'context'>;
71
+ /**
72
+ * Queue settings.
73
+ */
74
+ queue?: RateLimiterQueueOptions;
75
+ }
76
+ //# sourceMappingURL=rate-limiter-options.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limiter-options.js.map
@@ -0,0 +1,42 @@
1
+ import { type SelectionPolicy } from '@stimulcross/ds-policy-priority-queue';
2
+ /**
3
+ * Queue options for rate limiter.
4
+ *
5
+ * These options are applied to limiters that support delayed execution.
6
+ */
7
+ export interface RateLimiterQueueOptions {
8
+ /**
9
+ * Defines the maximum number of tasks that can be executed concurrently.
10
+ *
11
+ * @default Infinity
12
+ */
13
+ concurrency?: number;
14
+ /**
15
+ * Maximum time to wait in the queue (in milliseconds).
16
+ *
17
+ * If a task is not started within this time, it will be rejected with `EXPIRED` error code.
18
+ *
19
+ * @default Infinity
20
+ */
21
+ maxWaitMs?: number;
22
+ /**
23
+ * Maximum queue size.
24
+ *
25
+ * When overflowed, new tasks will be rejected immediately.
26
+ *
27
+ * @default Infinity
28
+ */
29
+ capacity?: number;
30
+ /**
31
+ * Selection policy for the priority queue.
32
+ *
33
+ * Defaults to Weighted round-robin (WRR) with the following weights:
34
+ * - `Priority.Lowest` - 1
35
+ * - `Priority.Low` - 2
36
+ * - `Priority.Normal` - 4
37
+ * - `Priority.High` - 8
38
+ * - `Priority.Highest` - 16
39
+ */
40
+ selectionPolicy?: SelectionPolicy;
41
+ }
42
+ //# sourceMappingURL=rate-limiter-queue-options.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limiter-queue-options.js.map
@@ -0,0 +1,52 @@
1
+ import { type Priority } from '@stimulcross/ds-policy-priority-queue';
2
+ import { type LimitBehavior } from '../types/limit-behavior.js';
3
+ /**
4
+ * Options for running a single task.
5
+ */
6
+ export interface RateLimiterRunOptions {
7
+ /**
8
+ * A unique identifier for the task for logging and debugging.
9
+ *
10
+ * If not provided, the library will generate a unique ID.
11
+ */
12
+ id?: string;
13
+ /**
14
+ * A storage key for the task.
15
+ */
16
+ key?: string;
17
+ /**
18
+ * The cost to consume the limit.
19
+ *
20
+ * @default 1
21
+ */
22
+ cost?: number;
23
+ /**
24
+ * Defines the behavior when the limit is reached for the current task. Overrides the global limit behavior
25
+ * set in {@link RateLimiterOptions.limitBehavior}.
26
+ *
27
+ * - `reject` - rejects the task with `LIMIT_EXCEEDED` error code
28
+ * - `enqueue` - enqueues the task if possible
29
+ *
30
+ * Defaults to global {@link RateLimiterOptions.limitBehavior}
31
+ */
32
+ limitBehavior?: LimitBehavior;
33
+ /**
34
+ * Task priority.
35
+ *
36
+ * @default Priority.Normal (3)
37
+ */
38
+ priority?: Priority;
39
+ /**
40
+ * An abort signal to abort the task execution.
41
+ */
42
+ signal?: AbortSignal;
43
+ /**
44
+ * Maximum wait time in milliseconds for the task in the queue.
45
+ *
46
+ * This does not affect execution time.
47
+ *
48
+ * @default Infinity
49
+ */
50
+ maxWaitMs?: number;
51
+ }
52
+ //# sourceMappingURL=rate-limiter-run-options.d.ts.map