@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
@@ -1,108 +0,0 @@
1
- import { LogLevel } from '@stimulcross/logger';
2
- import { GenericCellPolicy } from './generic-cell.policy.js';
3
- import { AbstractRateLimiter, type ExecutionContext } from '../abstract-rate-limiter.js';
4
- import { type GenericCellOptions } from './generic-cell.options.js';
5
- import { type GenericCellState } from './generic-cell.state.js';
6
- import { type GenericCellStatus } from './generic-cell.status.js';
7
- import { type Decision } from '../../core/decision.js';
8
- import { RateLimitErrorCode } from '../../enums/rate-limit-error-code.js';
9
- import { RateLimitError } from '../../errors/rate-limit.error.js';
10
- import { type LimitBehavior } from '../../types/limit-behavior.js';
11
-
12
- /**
13
- * Generic Cell (GCRA) rate limiter.
14
- *
15
- * Designed primarily for client-side use to respect third-party limits or protect resources.
16
- * While this can be used as a server-side limiter with custom distributed storage
17
- * (e.g., Redis), it is best-effort and not recommended due to high network round-trip latency.
18
- *
19
- * Key features:
20
- * - **Queueing & overflow** - optionally enqueues excess requests up to a maximum allowed overflow capacity
21
- * - **Concurrency** - limits how many requests can be executed simultaneously
22
- * - **Priority** - supports task priorities (with fairness and custom policy) to execute critical requests first
23
- * - **Cancellation** - supports `AbortSignal` to safely remove pending requests from the queue
24
- * - **Expiration** - automatically drops queued requests that wait longer than the allowed `maxWaitMs`
25
- * - **Auto-rollback** - reverts spent quota if an enqueued task is canceled or expired
26
- */
27
- export class GenericCellLimiter extends AbstractRateLimiter<GenericCellState, GenericCellStatus> {
28
- private readonly _defaultLimitBehaviour: LimitBehavior;
29
- private readonly _defaultMaxWaitMs: number | undefined;
30
-
31
- protected override readonly _policy: GenericCellPolicy;
32
-
33
- constructor(options: GenericCellOptions) {
34
- super(options);
35
-
36
- this._defaultLimitBehaviour = options.limitBehavior ?? 'reject';
37
-
38
- if (options.queue?.maxWaitMs) {
39
- this._defaultMaxWaitMs = options.queue.maxWaitMs;
40
- }
41
-
42
- this._policy = new GenericCellPolicy(options.intervalMs, options.burst);
43
- }
44
-
45
- protected override async _runInternal<T>(fn: () => T | Promise<T>, ctx: ExecutionContext): Promise<T> {
46
- const now = this._clock.now();
47
- const baseTtlMs: number = Math.ceil(this._policy.burst * this._policy.intervalMs);
48
-
49
- let runAt: number;
50
- let storeTtlMs: number;
51
-
52
- await this._store.acquireLock?.(ctx.key);
53
-
54
- try {
55
- const state = (await this._store.get(ctx.key)) ?? this._policy.getInitialState();
56
- const finalLimitBehavior = ctx.limitBehavior ?? this._defaultLimitBehaviour;
57
-
58
- const { decision, nextState } = this._policy.evaluate(
59
- state,
60
- now,
61
- ctx.cost,
62
- finalLimitBehavior === 'enqueue',
63
- );
64
-
65
- if (decision.kind === 'deny') {
66
- this._logger.debug(`[DENY] [id: ${ctx.id}, key: ${ctx.key}] - Retry: +${decision.retryAt - now}ms`);
67
- throw new RateLimitError(RateLimitErrorCode.LimitExceeded, decision.retryAt);
68
- }
69
-
70
- runAt = decision.kind === 'delay' ? decision.runAt : now;
71
- storeTtlMs = Math.max(baseTtlMs, runAt - now + baseTtlMs);
72
-
73
- await this._store.set(ctx.key, nextState, storeTtlMs);
74
-
75
- this._printSuccessDebug(decision, nextState, now, ctx);
76
- } finally {
77
- await this._store.releaseLock?.(ctx.key);
78
- }
79
-
80
- const finalMaxWaitMs = ctx.maxWaitMs ?? this._defaultMaxWaitMs;
81
- const expiresAt = finalMaxWaitMs ? now + finalMaxWaitMs : undefined;
82
-
83
- return await this._execute<T>(fn, runAt, storeTtlMs, ctx, expiresAt);
84
- }
85
-
86
- protected override _getDebugStateString(state: GenericCellState): string {
87
- return String(state.tat);
88
- }
89
-
90
- private _printSuccessDebug(
91
- decision: Decision,
92
- nextState: GenericCellState,
93
- now: number,
94
- ctx: ExecutionContext,
95
- ): void {
96
- if (this._logger.minLevel < LogLevel.DEBUG) {
97
- return;
98
- }
99
-
100
- if (decision.kind === 'delay') {
101
- this._logger.debug(
102
- `[DELAY] [id: ${ctx.id}, key: ${ctx.key}] +${decision.runAt - now}ms - tat: ${nextState.tat}`,
103
- );
104
- } else {
105
- this._logger.debug(`[ALLOW] [id: ${ctx.id}, key: ${ctx.key}] - tat: ${nextState.tat}`);
106
- }
107
- }
108
- }
@@ -1,23 +0,0 @@
1
- import { type GenericCellState } from './generic-cell.state.js';
2
- import { type RateLimiterOptions } from '../../interfaces/rate-limiter-options.js';
3
-
4
- /**
5
- * Options for the Generic Cell Rate Algorithm (GCRA) limiter.
6
- */
7
- export interface GenericCellOptions extends RateLimiterOptions<GenericCellState> {
8
- /**
9
- * The minimum interval between requests (in milliseconds).
10
- *
11
- * This defines the emission interval.
12
- * For example, if you want to allow 10 requests per second, set this to 100ms (1000ms / 10).
13
- */
14
- intervalMs: number;
15
-
16
- /**
17
- * The maximum burst size.
18
- *
19
- * This defines how many requests can be made immediately in quick succession before rate limiting kicks in.
20
- * The burst capacity allows temporary spikes in traffic.
21
- */
22
- burst: number;
23
- }
@@ -1,115 +0,0 @@
1
- import { type GenericCellState } from './generic-cell.state.js';
2
- import { type GenericCellStatus } from './generic-cell.status.js';
3
- import { type RateLimitPolicy, type RateLimitPolicyResult } from '../../core/rate-limit-policy.js';
4
- import { validateCost } from '../../utils/validate-cost.js';
5
-
6
- /** @internal */
7
- export class GenericCellPolicy implements RateLimitPolicy<GenericCellState, GenericCellStatus> {
8
- constructor(
9
- private readonly _intervalMs: number,
10
- private readonly _burst: number,
11
- private readonly _maxDelayMs: number = Number.POSITIVE_INFINITY,
12
- ) {
13
- if (!Number.isFinite(_intervalMs) || !Number.isSafeInteger(_intervalMs) || _intervalMs <= 0) {
14
- throw new Error(`Invalid intervalMs: ${_intervalMs}. Must be a positive integer.`);
15
- }
16
-
17
- if (!Number.isFinite(_burst) || !Number.isSafeInteger(_burst) || _burst <= 0) {
18
- throw new Error(`Invalid burst: ${_burst}. Must be a positive integer.`);
19
- }
20
-
21
- if (_maxDelayMs < 0 || (!Number.isSafeInteger(_maxDelayMs) && _maxDelayMs !== Number.POSITIVE_INFINITY)) {
22
- throw new Error(`Invalid maxDelayMs: ${_maxDelayMs}. Must be a non-negative integer or Infinity.`);
23
- }
24
- }
25
-
26
- public get burst(): number {
27
- return this._burst;
28
- }
29
-
30
- public get intervalMs(): number {
31
- return this._intervalMs;
32
- }
33
-
34
- public getInitialState(): GenericCellState {
35
- return { tat: 0 };
36
- }
37
-
38
- public getStatus(state: GenericCellState, now: number): GenericCellStatus {
39
- const tat = Math.max(state.tat, now);
40
- const burstOffset = this._burst * this._intervalMs;
41
-
42
- const availableTimeDebt = burstOffset + now - tat;
43
- const remaining = Math.max(0, Math.floor(availableTimeDebt / this._intervalMs));
44
-
45
- const nextAvailableAt = Math.max(now, tat + this._intervalMs - burstOffset);
46
-
47
- const resetAt = tat;
48
-
49
- return {
50
- intervalMs: this._intervalMs,
51
- burst: this._burst,
52
- tat,
53
- remaining,
54
- nextAvailableAt,
55
- resetAt,
56
- };
57
- }
58
-
59
- public evaluate(
60
- state: GenericCellState,
61
- now: number,
62
- cost: number,
63
- shouldReserve?: boolean,
64
- ): RateLimitPolicyResult<GenericCellState> {
65
- const absoluteMax = shouldReserve ? this._burst + Math.floor(this._maxDelayMs / this._intervalMs) : this._burst;
66
-
67
- validateCost(cost, absoluteMax);
68
-
69
- const tat = Math.max(state.tat, now);
70
- const costInterval = cost * this._intervalMs;
71
- const burstOffset = this._burst * this._intervalMs;
72
-
73
- const newTat = tat + costInterval;
74
-
75
- const minNow = newTat - burstOffset;
76
-
77
- if (now >= minNow) {
78
- return {
79
- decision: { kind: 'allow' },
80
- nextState: { tat: newTat },
81
- };
82
- }
83
-
84
- if (shouldReserve) {
85
- const waitMs = minNow - now;
86
-
87
- if (waitMs > this._maxDelayMs) {
88
- const retryAt = minNow - this._maxDelayMs;
89
- return this._deny(state.tat, retryAt);
90
- }
91
-
92
- const runAt = minNow;
93
- return {
94
- decision: { kind: 'delay', runAt },
95
- nextState: { tat: newTat },
96
- };
97
- }
98
-
99
- return this._deny(state.tat, minNow);
100
- }
101
-
102
- public revert(state: GenericCellState, cost: number): GenericCellState {
103
- const costInterval = cost * this._intervalMs;
104
- return {
105
- tat: Math.max(0, state.tat - costInterval),
106
- };
107
- }
108
-
109
- private _deny(tat: number, retryAt: number): RateLimitPolicyResult<GenericCellState> {
110
- return {
111
- decision: { kind: 'deny', retryAt },
112
- nextState: { tat },
113
- };
114
- }
115
- }
@@ -1,54 +0,0 @@
1
- import { type RateLimiterStatus } from '../../core/rate-limiter-status.js';
2
-
3
- /**
4
- * The status of the Generic Cell rate limiter.
5
- */
6
- export interface GenericCellStatus extends RateLimiterStatus {
7
- /**
8
- * The minimum interval between tokens (in milliseconds).
9
- *
10
- * It represents the emission interval - the time period between
11
- * successive token emissions at the steady-state rate.
12
- */
13
- readonly intervalMs: number;
14
-
15
- /**
16
- * The maximum burst size (maximum number of tokens that can be accumulated).
17
- *
18
- * It represents the burst capacity - the maximum number of requests
19
- * that can be made instantaneously when the bucket is full.
20
- */
21
- readonly burst: number;
22
-
23
- /**
24
- * Theoretical Arrival Time (TAT) - the virtual time when the bucket will be empty.
25
- *
26
- * TAT represents the earliest time at which a future request could be allowed.
27
- *
28
- * Value is in milliseconds.
29
- */
30
- readonly tat: number;
31
-
32
- /**
33
- * The number of tokens that can be used immediately.
34
- *
35
- * Calculated as the time allowance available between now and TAT, divided by
36
- * the emission interval.
37
- */
38
- readonly remaining: number;
39
-
40
- /**
41
- * The timestamp (in milliseconds) when the next token will be available for use.
42
- *
43
- * This is the earliest time when a request can be accepted if no tokens are currently available.
44
- */
45
- readonly nextAvailableAt: number;
46
-
47
- /**
48
- * The timestamp when all tokens will be available for use again.
49
- *
50
- * It is when the TAT returns to the current time, meaning the bucket has fully recovered to its
51
- * maximum burst capacity.
52
- */
53
- readonly resetAt: number;
54
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Rate limit information extracted from the HTTP response headers.
3
- */
4
- export interface HttpLimitInfo {
5
- /**
6
- * The maximum number of requests allowed within the time window.
7
- *
8
- * It usually corresponds to `RateLimit-Limit` or `X-RateLimit-Limit` headers.
9
- */
10
- limit: number;
11
-
12
- /**
13
- * The number of requests remaining in the current time window.
14
- *
15
- * It usually corresponds to `RateLimit-Remaining` or `X-RateLimit-Remaining` headers.
16
- */
17
- remaining: number;
18
-
19
- /**
20
- * The timestamp (in milliseconds) when the current time window resets.
21
- *
22
- * It usually corresponds to `RateLimit-Reset`, `X-RateLimit-Reset`, or `Retry-After` headers.
23
- *
24
- * **WARNING:** these headers can be either delta (usually in seconds) or UNIX epoche
25
- * timestamp (usually in seconds) depending on the target API specs.
26
- *
27
- *The library expects a UNIX epoche timestamp in milliseconds. Refer to the API docs to
28
- * determine the header format and properly convert it to UNIX epoche timestamp in milliseconds.
29
- *
30
- * For example,
31
- * - delta in seconds: `Date.now() + delta * 1000`
32
- * - UNIX timestamp in seconds: `timestamp * 1000`
33
- *
34
- */
35
- resetAt?: number | null;
36
-
37
- /**
38
- * HTTP status code of the response.
39
- */
40
- statusCode: number;
41
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * The state of the HTTP Response Based rate limiter.
3
- *
4
- * When using a distributed state store, make sure it properly serializes and deserializes the state.
5
- */
6
- export interface HttpResponseBasedLimiterState {
7
- isProbing: boolean;
8
- isUnlimited: boolean;
9
- lastKnownLimit: number | null;
10
- lastKnownRemaining: number | null;
11
- lastKnownResetAt: number | null;
12
- lastSyncedAt: number | null;
13
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * The status of the HTTP Response Based rate limiter.
3
- *
4
- * This interface represents rate limit information extracted from HTTP response headers,
5
- * such as RateLimit headers (RFC 6585) or custom headers provided by APIs (X-RateLimit-*).
6
- *
7
- * @remarks
8
- * The status is updated after each API response and reflects the server-side rate limit state.
9
- * Can be useful to check how many requests remain before hitting the rate limit.
10
- */
11
- export interface HttpResponseBasedLimiterStatus {
12
- /**
13
- * Indicates whether the rate limiter is currently probing for the server's limit state.
14
- *
15
- * This is `true` when the rate limiter is waiting for the first response from the server
16
- * to extract rate limit headers.
17
- */
18
- isProbing: boolean;
19
-
20
- /**
21
- * Indicates whether the rate limiter is in unlimited mode.
22
- *
23
- * This is `true` if a server did not send any rate limit headers.
24
- *
25
- * If this is `true`, the `lastKnownLimit`, `lastKnownRemaining`, and `lastKnownResetAt` values are `null`.
26
- */
27
- isUnlimited: boolean;
28
-
29
- /**
30
- * The number of requests remaining in the current rate limit window.
31
- *
32
- * @remarks
33
- * This value is extracted from response headers such as `X-RateLimit-Remaining`
34
- * or `RateLimit-Remaining`. It decrements with each request and resets when
35
- * the time window expires.
36
- *
37
- * When this reaches 0, later requests may be delayed or rejected until the reset time.
38
- */
39
- lastKnownRemaining: number | null;
40
-
41
- /**
42
- * The maximum number of requests allowed in the current rate limit window.
43
- *
44
- * @remarks
45
- * This value is extracted from response headers such as `X-RateLimit-Limit`
46
- * or `RateLimit-Limit`. It represents the total quota allocated by the API
47
- * and typically remains constant across requests within the same window.
48
- */
49
- lastKnownLimit: number | null;
50
-
51
- /**
52
- * The timestamp (in milliseconds since Unix epoch) when the rate limit window resets.
53
- *
54
- * `null` if the reset time is not known or not applicable.
55
- *
56
- * @remarks
57
- * This value is extracted from response headers such as `X-RateLimit-Reset`
58
- * or `RateLimit-Reset`. The exact semantics depend on the API specification:
59
- *
60
- * - **Full window reset**: When the entire rate limit quota is restored
61
- * - **Next request availability**: When the next single request slot becomes available
62
- * - **Sliding window**: When the oldest request in the window expires
63
- *
64
- * Always refer to your API's documentation to understand the reset behavior.
65
- */
66
- lastKnownResetAt: number | null;
67
-
68
- /**
69
- * The timestamp (in milliseconds since Unix epoch) when the status was last synced with the server.
70
- *
71
- * `null` if the status has never been synced.
72
- */
73
- lastSyncedAt: number | null;
74
- }