@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.
- package/README.md +20 -0
- package/lib/core/cancellable.d.ts +5 -0
- package/lib/core/cancellable.js +2 -0
- package/lib/core/clock.d.ts +10 -0
- package/lib/core/clock.js +2 -0
- package/{src/core/decision.ts → lib/core/decision.d.ts} +7 -11
- package/lib/core/decision.js +2 -0
- package/lib/core/rate-limit-policy.d.ts +14 -0
- package/lib/core/rate-limit-policy.js +2 -0
- package/lib/core/rate-limiter-status.d.ts +14 -0
- package/lib/core/rate-limiter-status.js +2 -0
- package/lib/core/rate-limiter.d.ts +34 -0
- package/lib/core/rate-limiter.js +2 -0
- package/lib/core/state-storage.d.ts +46 -0
- package/lib/core/state-storage.js +2 -0
- package/lib/enums/rate-limit-error-code.d.ts +26 -0
- package/lib/enums/rate-limit-error-code.js +27 -0
- package/lib/errors/custom.error.d.ts +6 -0
- package/lib/errors/custom.error.js +13 -0
- package/lib/errors/invalid-cost.error.d.ts +16 -0
- package/lib/errors/invalid-cost.error.js +26 -0
- package/lib/errors/rate-limit.error.d.ts +37 -0
- package/lib/errors/rate-limit.error.js +75 -0
- package/lib/errors/rate-limiter-destroyed.error.d.ts +7 -0
- package/lib/errors/rate-limiter-destroyed.error.js +9 -0
- package/{src/index.ts → lib/index.d.ts} +1 -0
- package/lib/index.js +5 -0
- package/lib/interfaces/rate-limiter-options.d.ts +76 -0
- package/lib/interfaces/rate-limiter-options.js +2 -0
- package/lib/interfaces/rate-limiter-queue-options.d.ts +42 -0
- package/lib/interfaces/rate-limiter-queue-options.js +2 -0
- package/lib/interfaces/rate-limiter-run-options.d.ts +52 -0
- package/lib/interfaces/rate-limiter-run-options.js +2 -0
- package/lib/limiters/abstract-rate-limiter.d.ts +44 -0
- package/lib/limiters/abstract-rate-limiter.js +133 -0
- package/lib/limiters/composite.policy.d.ts +15 -0
- package/lib/limiters/composite.policy.js +73 -0
- package/lib/limiters/fixed-window/fixed-window.limiter.d.ts +33 -0
- package/lib/limiters/fixed-window/fixed-window.limiter.js +85 -0
- package/lib/limiters/fixed-window/fixed-window.options.d.ts +27 -0
- package/lib/limiters/fixed-window/fixed-window.options.js +2 -0
- package/lib/limiters/fixed-window/fixed-window.policy.d.ts +19 -0
- package/lib/limiters/fixed-window/fixed-window.policy.js +121 -0
- package/{src/limiters/fixed-window/fixed-window.state.ts → lib/limiters/fixed-window/fixed-window.state.d.ts} +4 -3
- package/lib/limiters/fixed-window/fixed-window.state.js +2 -0
- package/lib/limiters/fixed-window/fixed-window.status.d.ts +39 -0
- package/lib/limiters/fixed-window/fixed-window.status.js +2 -0
- package/{src/limiters/fixed-window/index.ts → lib/limiters/fixed-window/index.d.ts} +1 -0
- package/lib/limiters/fixed-window/index.js +2 -0
- package/lib/limiters/generic-cell/generic-cell.limiter.d.ts +30 -0
- package/lib/limiters/generic-cell/generic-cell.limiter.js +74 -0
- package/lib/limiters/generic-cell/generic-cell.options.d.ts +22 -0
- package/lib/limiters/generic-cell/generic-cell.options.js +2 -0
- package/lib/limiters/generic-cell/generic-cell.policy.d.ts +18 -0
- package/lib/limiters/generic-cell/generic-cell.policy.js +87 -0
- package/{src/limiters/generic-cell/generic-cell.state.ts → lib/limiters/generic-cell/generic-cell.state.d.ts} +2 -1
- package/lib/limiters/generic-cell/generic-cell.state.js +2 -0
- package/lib/limiters/generic-cell/generic-cell.status.d.ts +49 -0
- package/lib/limiters/generic-cell/generic-cell.status.js +2 -0
- package/{src/limiters/generic-cell/index.ts → lib/limiters/generic-cell/index.d.ts} +1 -0
- package/lib/limiters/generic-cell/index.js +2 -0
- package/{src/limiters/http-response-based/http-limit-info.extractor.ts → lib/limiters/http-response-based/http-limit-info.extractor.d.ts} +2 -6
- package/lib/limiters/http-response-based/http-limit-info.extractor.js +2 -0
- package/lib/limiters/http-response-based/http-limit.info.d.ts +39 -0
- package/lib/limiters/http-response-based/http-limit.info.js +2 -0
- 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
- package/lib/limiters/http-response-based/http-response-based-limiter.options.js +2 -0
- package/lib/limiters/http-response-based/http-response-based-limiter.state.d.ts +14 -0
- package/lib/limiters/http-response-based/http-response-based-limiter.state.js +2 -0
- package/lib/limiters/http-response-based/http-response-based-limiter.status.d.ts +70 -0
- package/lib/limiters/http-response-based/http-response-based-limiter.status.js +2 -0
- package/lib/limiters/http-response-based/http-response-based.limiter.d.ts +56 -0
- package/lib/limiters/http-response-based/http-response-based.limiter.js +386 -0
- package/{src/limiters/http-response-based/index.ts → lib/limiters/http-response-based/index.d.ts} +1 -0
- package/lib/limiters/http-response-based/index.js +2 -0
- package/{src/limiters/leaky-bucket/index.ts → lib/limiters/leaky-bucket/index.d.ts} +1 -0
- package/lib/limiters/leaky-bucket/index.js +2 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.limiter.d.ts +30 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.limiter.js +75 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.options.d.ts +22 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.options.js +2 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.policy.d.ts +19 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.policy.js +101 -0
- package/{src/limiters/leaky-bucket/leaky-bucket.state.ts → lib/limiters/leaky-bucket/leaky-bucket.state.d.ts} +3 -2
- package/lib/limiters/leaky-bucket/leaky-bucket.state.js +2 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.status.d.ts +31 -0
- package/lib/limiters/leaky-bucket/leaky-bucket.status.js +2 -0
- package/{src/limiters/sliding-window-counter/index.ts → lib/limiters/sliding-window-counter/index.d.ts} +2 -4
- package/lib/limiters/sliding-window-counter/index.js +2 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.limiter.d.ts +28 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.limiter.js +47 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.options.d.ts +16 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.options.js +2 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.policy.d.ts +18 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.policy.js +128 -0
- package/{src/limiters/sliding-window-counter/sliding-window-counter.state.ts → lib/limiters/sliding-window-counter/sliding-window-counter.state.d.ts} +4 -3
- package/lib/limiters/sliding-window-counter/sliding-window-counter.state.js +2 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.status.d.ts +45 -0
- package/lib/limiters/sliding-window-counter/sliding-window-counter.status.js +2 -0
- package/{src/limiters/sliding-window-log/index.ts → lib/limiters/sliding-window-log/index.d.ts} +1 -0
- package/lib/limiters/sliding-window-log/index.js +2 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.limiter.d.ts +27 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.limiter.js +44 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.options.d.ts +16 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.options.js +2 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.policy.d.ts +18 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.policy.js +124 -0
- package/{src/limiters/sliding-window-log/sliding-window-log.state.ts → lib/limiters/sliding-window-log/sliding-window-log.state.d.ts} +5 -6
- package/lib/limiters/sliding-window-log/sliding-window-log.state.js +2 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.status.d.ts +39 -0
- package/lib/limiters/sliding-window-log/sliding-window-log.status.js +2 -0
- package/{src/limiters/token-bucket/index.ts → lib/limiters/token-bucket/index.d.ts} +1 -0
- package/lib/limiters/token-bucket/index.js +2 -0
- package/lib/limiters/token-bucket/token-bucket.limiter.d.ts +30 -0
- package/lib/limiters/token-bucket/token-bucket.limiter.js +75 -0
- package/{src/limiters/token-bucket/token-bucket.options.ts → lib/limiters/token-bucket/token-bucket.options.d.ts} +9 -10
- package/lib/limiters/token-bucket/token-bucket.options.js +2 -0
- package/lib/limiters/token-bucket/token-bucket.policy.d.ts +19 -0
- package/lib/limiters/token-bucket/token-bucket.policy.js +116 -0
- package/{src/limiters/token-bucket/token-bucket.state.ts → lib/limiters/token-bucket/token-bucket.state.d.ts} +4 -3
- package/lib/limiters/token-bucket/token-bucket.state.js +2 -0
- package/lib/limiters/token-bucket/token-bucket.status.d.ts +31 -0
- package/lib/limiters/token-bucket/token-bucket.status.js +2 -0
- package/lib/runtime/default-clock.d.ts +4 -0
- package/lib/runtime/default-clock.js +7 -0
- package/lib/runtime/execution-tickets.d.ts +12 -0
- package/lib/runtime/execution-tickets.js +27 -0
- package/lib/runtime/in-memory-state-store.d.ts +19 -0
- package/lib/runtime/in-memory-state-store.js +97 -0
- package/lib/runtime/rate-limiter.executor.d.ts +47 -0
- package/lib/runtime/rate-limiter.executor.js +196 -0
- package/lib/runtime/semaphore.d.ts +9 -0
- package/lib/runtime/semaphore.js +28 -0
- package/lib/runtime/task.d.ts +41 -0
- package/lib/runtime/task.js +101 -0
- package/{src/types/limit-behavior.ts → lib/types/limit-behavior.d.ts} +1 -0
- package/lib/types/limit-behavior.js +2 -0
- package/lib/utils/generate-random-string.d.ts +3 -0
- package/lib/utils/generate-random-string.js +13 -0
- package/lib/utils/promise-with-resolvers.d.ts +9 -0
- package/lib/utils/promise-with-resolvers.js +15 -0
- package/lib/utils/sanitize-error.d.ts +3 -0
- package/lib/utils/sanitize-error.js +5 -0
- package/lib/utils/sanitize-priority.d.ts +4 -0
- package/lib/utils/sanitize-priority.js +18 -0
- package/lib/utils/validate-cost.d.ts +3 -0
- package/lib/utils/validate-cost.js +14 -0
- package/package.json +13 -2
- package/.editorconfig +0 -21
- package/.github/workflows/node.yml +0 -87
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/.megaignore +0 -8
- package/.prettierignore +0 -3
- package/commitlint.config.js +0 -8
- package/eslint.config.js +0 -65
- package/lint-staged.config.js +0 -4
- package/prettier.config.cjs +0 -1
- package/src/core/cancellable.ts +0 -4
- package/src/core/clock.ts +0 -9
- package/src/core/rate-limit-policy.ts +0 -15
- package/src/core/rate-limiter-status.ts +0 -14
- package/src/core/rate-limiter.ts +0 -37
- package/src/core/state-storage.ts +0 -51
- package/src/enums/rate-limit-error-code.ts +0 -29
- package/src/errors/custom.error.ts +0 -14
- package/src/errors/invalid-cost.error.ts +0 -33
- package/src/errors/rate-limit.error.ts +0 -91
- package/src/errors/rate-limiter-destroyed.error.ts +0 -8
- package/src/interfaces/rate-limiter-options.ts +0 -84
- package/src/interfaces/rate-limiter-queue-options.ts +0 -45
- package/src/interfaces/rate-limiter-run-options.ts +0 -58
- package/src/limiters/abstract-rate-limiter.ts +0 -206
- package/src/limiters/composite.policy.ts +0 -102
- package/src/limiters/fixed-window/fixed-window.limiter.ts +0 -121
- package/src/limiters/fixed-window/fixed-window.options.ts +0 -29
- package/src/limiters/fixed-window/fixed-window.policy.ts +0 -159
- package/src/limiters/fixed-window/fixed-window.status.ts +0 -46
- package/src/limiters/generic-cell/generic-cell.limiter.ts +0 -108
- package/src/limiters/generic-cell/generic-cell.options.ts +0 -23
- package/src/limiters/generic-cell/generic-cell.policy.ts +0 -115
- package/src/limiters/generic-cell/generic-cell.status.ts +0 -54
- package/src/limiters/http-response-based/http-limit.info.ts +0 -41
- package/src/limiters/http-response-based/http-response-based-limiter.state.ts +0 -13
- package/src/limiters/http-response-based/http-response-based-limiter.status.ts +0 -74
- package/src/limiters/http-response-based/http-response-based.limiter.ts +0 -512
- package/src/limiters/leaky-bucket/leaky-bucket.limiter.ts +0 -105
- package/src/limiters/leaky-bucket/leaky-bucket.options.ts +0 -23
- package/src/limiters/leaky-bucket/leaky-bucket.policy.ts +0 -134
- package/src/limiters/leaky-bucket/leaky-bucket.status.ts +0 -36
- package/src/limiters/sliding-window-counter/sliding-window-counter.limiter.ts +0 -76
- package/src/limiters/sliding-window-counter/sliding-window-counter.options.ts +0 -20
- package/src/limiters/sliding-window-counter/sliding-window-counter.policy.ts +0 -167
- package/src/limiters/sliding-window-counter/sliding-window-counter.status.ts +0 -53
- package/src/limiters/sliding-window-log/sliding-window-log.limiter.ts +0 -65
- package/src/limiters/sliding-window-log/sliding-window-log.options.ts +0 -20
- package/src/limiters/sliding-window-log/sliding-window-log.policy.ts +0 -166
- package/src/limiters/sliding-window-log/sliding-window-log.status.ts +0 -44
- package/src/limiters/token-bucket/token-bucket.limiter.ts +0 -110
- package/src/limiters/token-bucket/token-bucket.policy.ts +0 -155
- package/src/limiters/token-bucket/token-bucket.status.ts +0 -36
- package/src/runtime/default-clock.ts +0 -8
- package/src/runtime/execution-tickets.ts +0 -34
- package/src/runtime/in-memory-state-store.ts +0 -135
- package/src/runtime/rate-limiter.executor.ts +0 -286
- package/src/runtime/semaphore.ts +0 -31
- package/src/runtime/task.ts +0 -141
- package/src/utils/generate-random-string.ts +0 -16
- package/src/utils/promise-with-resolvers.ts +0 -23
- package/src/utils/sanitize-error.ts +0 -4
- package/src/utils/sanitize-priority.ts +0 -22
- package/src/utils/validate-cost.ts +0 -16
- package/tests/integration/limiters/fixed-window.limiter.spec.ts +0 -371
- package/tests/integration/limiters/generic-cell.limiter.spec.ts +0 -361
- package/tests/integration/limiters/http-response-based.limiter.spec.ts +0 -833
- package/tests/integration/limiters/leaky-bucket.spec.ts +0 -357
- package/tests/integration/limiters/sliding-window-counter.limiter.spec.ts +0 -175
- package/tests/integration/limiters/sliding-window-log.spec.ts +0 -185
- package/tests/integration/limiters/token-bucket.limiter.spec.ts +0 -363
- package/tests/tsconfig.json +0 -4
- package/tests/unit/policies/composite.policy.spec.ts +0 -244
- package/tests/unit/policies/fixed-window.policy.spec.ts +0 -260
- package/tests/unit/policies/generic-cell.policy.spec.ts +0 -178
- package/tests/unit/policies/leaky-bucket.policy.spec.ts +0 -215
- package/tests/unit/policies/sliding-window-counter.policy.spec.ts +0 -209
- package/tests/unit/policies/sliding-window-log.policy.spec.ts +0 -285
- package/tests/unit/policies/token-bucket.policy.spec.ts +0 -371
- package/tests/unit/runtime/execution-tickets.spec.ts +0 -121
- package/tests/unit/runtime/in-memory-state-store.spec.ts +0 -238
- package/tests/unit/runtime/rate-limiter.executor.spec.ts +0 -353
- package/tests/unit/runtime/semaphore.spec.ts +0 -98
- package/tests/unit/runtime/task.spec.ts +0 -182
- package/tests/unit/utils/generate-random-string.spec.ts +0 -51
- package/tests/unit/utils/promise-with-resolvers.spec.ts +0 -57
- package/tests/unit/utils/sanitize-priority.spec.ts +0 -46
- package/tests/unit/utils/validate-cost.spec.ts +0 -48
- package/tsconfig.json +0 -14
- package/vitest.config.js +0 -22
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type RateLimiterStatus } from '../../core/rate-limiter-status.js';
|
|
2
|
+
/**
|
|
3
|
+
* The status of the Sliding Window Log rate limiter.
|
|
4
|
+
*/
|
|
5
|
+
export interface SlidingWindowLogStatus extends RateLimiterStatus {
|
|
6
|
+
/**
|
|
7
|
+
* Maximum number of requests allowed within the time window.
|
|
8
|
+
*/
|
|
9
|
+
limit: number;
|
|
10
|
+
/**
|
|
11
|
+
* The sliding time window duration in milliseconds.
|
|
12
|
+
*/
|
|
13
|
+
windowMs: number;
|
|
14
|
+
/**
|
|
15
|
+
* Total number of requests consumed within the current sliding window.
|
|
16
|
+
*
|
|
17
|
+
* This count includes all requests that fall within the window period.
|
|
18
|
+
*/
|
|
19
|
+
totalUsed: number;
|
|
20
|
+
/**
|
|
21
|
+
* Number of remaining requests available before hitting the limit.
|
|
22
|
+
*/
|
|
23
|
+
remaining: number;
|
|
24
|
+
/**
|
|
25
|
+
* The timestamp (in milliseconds) when the next request slot will become available.
|
|
26
|
+
*
|
|
27
|
+
* This is based on when the oldest request in the window will expire.
|
|
28
|
+
*/
|
|
29
|
+
nextAvailableAt: number;
|
|
30
|
+
/**
|
|
31
|
+
* The timestamp (in milliseconds) when the current window will fully reset.
|
|
32
|
+
*
|
|
33
|
+
* Represents when the last (most recent) request in the log will expire.
|
|
34
|
+
*
|
|
35
|
+
* After this time, all requests will have aged out of the sliding window.
|
|
36
|
+
*/
|
|
37
|
+
resetAt: number;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=sliding-window-log.status.d.ts.map
|
|
@@ -2,3 +2,4 @@ export type { TokenBucketState } from './token-bucket.state.js';
|
|
|
2
2
|
export type { TokenBucketStatus } from './token-bucket.status.js';
|
|
3
3
|
export type { TokenBucketOptions } from './token-bucket.options.js';
|
|
4
4
|
export { TokenBucketLimiter } from './token-bucket.limiter.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type TokenBucketOptions } from './token-bucket.options.js';
|
|
2
|
+
import { TokenBucketPolicy } from './token-bucket.policy.js';
|
|
3
|
+
import { type TokenBucketState } from './token-bucket.state.js';
|
|
4
|
+
import { type TokenBucketStatus } from './token-bucket.status.js';
|
|
5
|
+
import { AbstractRateLimiter, type ExecutionContext } from '../abstract-rate-limiter.js';
|
|
6
|
+
/**
|
|
7
|
+
* Token Bucket rate limiter.
|
|
8
|
+
*
|
|
9
|
+
* Designed primarily for client-side use to respect third-party limits or protect resources.
|
|
10
|
+
* While this can be used as a server-side limiter with custom distributed storage
|
|
11
|
+
* (e.g., Redis), it is best-effort and not recommended due to high network round-trip latency.
|
|
12
|
+
*
|
|
13
|
+
* Key features:
|
|
14
|
+
* - **Queueing & overflow** - optionally enqueues excess requests up to a maximum allowed overflow capacity
|
|
15
|
+
* - **Concurrency** - limits how many requests can be executed simultaneously
|
|
16
|
+
* - **Priority** - supports task priorities (with fairness and custom policy) to execute critical requests first
|
|
17
|
+
* - **Cancellation** - supports `AbortSignal` to safely remove pending requests from the queue
|
|
18
|
+
* - **Expiration** - automatically drops queued requests that wait longer than the allowed `maxWaitMs`
|
|
19
|
+
* - **Auto-rollback** - reverts spent quota if an enqueued task is canceled or expired
|
|
20
|
+
*/
|
|
21
|
+
export declare class TokenBucketLimiter extends AbstractRateLimiter<TokenBucketState, TokenBucketStatus> {
|
|
22
|
+
private readonly _defaultLimitBehaviour;
|
|
23
|
+
private readonly _maxWaitMs;
|
|
24
|
+
protected readonly _policy: TokenBucketPolicy;
|
|
25
|
+
constructor(options: TokenBucketOptions);
|
|
26
|
+
protected _runInternal<T>(fn: () => T | Promise<T>, ctx: ExecutionContext): Promise<T>;
|
|
27
|
+
protected _getDebugStateString(state: TokenBucketState): string;
|
|
28
|
+
private _printSuccessDebug;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=token-bucket.limiter.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { LogLevel } from '@stimulcross/logger';
|
|
2
|
+
import { TokenBucketPolicy } from './token-bucket.policy.js';
|
|
3
|
+
import { RateLimitErrorCode } from '../../enums/rate-limit-error-code.js';
|
|
4
|
+
import { RateLimitError } from '../../errors/rate-limit.error.js';
|
|
5
|
+
import { AbstractRateLimiter } from '../abstract-rate-limiter.js';
|
|
6
|
+
/**
|
|
7
|
+
* Token Bucket rate limiter.
|
|
8
|
+
*
|
|
9
|
+
* Designed primarily for client-side use to respect third-party limits or protect resources.
|
|
10
|
+
* While this can be used as a server-side limiter with custom distributed storage
|
|
11
|
+
* (e.g., Redis), it is best-effort and not recommended due to high network round-trip latency.
|
|
12
|
+
*
|
|
13
|
+
* Key features:
|
|
14
|
+
* - **Queueing & overflow** - optionally enqueues excess requests up to a maximum allowed overflow capacity
|
|
15
|
+
* - **Concurrency** - limits how many requests can be executed simultaneously
|
|
16
|
+
* - **Priority** - supports task priorities (with fairness and custom policy) to execute critical requests first
|
|
17
|
+
* - **Cancellation** - supports `AbortSignal` to safely remove pending requests from the queue
|
|
18
|
+
* - **Expiration** - automatically drops queued requests that wait longer than the allowed `maxWaitMs`
|
|
19
|
+
* - **Auto-rollback** - reverts spent quota if an enqueued task is canceled or expired
|
|
20
|
+
*/
|
|
21
|
+
export class TokenBucketLimiter extends AbstractRateLimiter {
|
|
22
|
+
_defaultLimitBehaviour;
|
|
23
|
+
_maxWaitMs;
|
|
24
|
+
_policy;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
super(options);
|
|
27
|
+
this._defaultLimitBehaviour = options.limitBehavior ?? 'reject';
|
|
28
|
+
if (options.queue?.maxWaitMs) {
|
|
29
|
+
this._maxWaitMs = options.queue.maxWaitMs;
|
|
30
|
+
}
|
|
31
|
+
this._policy = new TokenBucketPolicy(options.capacity, options.refillRate);
|
|
32
|
+
}
|
|
33
|
+
async _runInternal(fn, ctx) {
|
|
34
|
+
const now = this._clock.now();
|
|
35
|
+
const baseTtlMs = Math.ceil(this._policy.capacity / (this._policy.refillRate / 1000));
|
|
36
|
+
let runAt;
|
|
37
|
+
let storeTtlMs;
|
|
38
|
+
await this._store.acquireLock?.(ctx.key);
|
|
39
|
+
try {
|
|
40
|
+
const state = (await this._store.get(ctx.key)) ?? this._policy.getInitialState();
|
|
41
|
+
const finalLimitBehavior = ctx.limitBehavior ?? this._defaultLimitBehaviour;
|
|
42
|
+
const { decision, nextState } = this._policy.evaluate(state, now, ctx.cost, finalLimitBehavior === 'enqueue');
|
|
43
|
+
if (decision.kind === 'deny') {
|
|
44
|
+
this._logger.debug(`[DENY] [id: ${ctx.id}, key: ${ctx.key}] - Retry: +${decision.retryAt - now}ms`);
|
|
45
|
+
throw new RateLimitError(RateLimitErrorCode.LimitExceeded, decision.retryAt);
|
|
46
|
+
}
|
|
47
|
+
runAt = decision.kind === 'delay' ? decision.runAt : now;
|
|
48
|
+
storeTtlMs = Math.max(baseTtlMs, baseTtlMs + (runAt - now));
|
|
49
|
+
await this._store.set(ctx.key, nextState, storeTtlMs);
|
|
50
|
+
this._printSuccessDebug(decision, nextState, now, ctx);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
await this._store.releaseLock?.(ctx.key);
|
|
54
|
+
}
|
|
55
|
+
const finalMaxWaitMs = ctx.maxWaitMs ?? this._maxWaitMs;
|
|
56
|
+
const expiresAt = finalMaxWaitMs ? now + finalMaxWaitMs : undefined;
|
|
57
|
+
return await this._execute(fn, runAt, storeTtlMs, ctx, expiresAt);
|
|
58
|
+
}
|
|
59
|
+
_getDebugStateString(state) {
|
|
60
|
+
return `tkn: ${state.tokens.toFixed(2)}/${this._policy.capacity}, deb: ${state.debt.toFixed(2)}`;
|
|
61
|
+
}
|
|
62
|
+
_printSuccessDebug(decision, nextState, now, ctx) {
|
|
63
|
+
if (this._logger.minLevel < LogLevel.DEBUG) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const debugStateString = this._getDebugStateString(nextState);
|
|
67
|
+
if (decision.kind === 'delay') {
|
|
68
|
+
this._logger.debug(`[DELAY] [id: ${ctx.id}, key: ${ctx.key}] +${decision.runAt - now}ms - ${debugStateString}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this._logger.debug(`[ALLOW] [id: ${ctx.id}, key: ${ctx.key}] - ${debugStateString}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=token-bucket.limiter.js.map
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { type TokenBucketState } from './token-bucket.state.js';
|
|
2
2
|
import { type RateLimiterOptions } from '../../interfaces/rate-limiter-options.js';
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* Options for the Token Bucket rate limiter.
|
|
6
5
|
*/
|
|
7
6
|
export interface TokenBucketOptions extends RateLimiterOptions<TokenBucketState> {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
refillRate: number;
|
|
7
|
+
/**
|
|
8
|
+
* The maximum number of tokens that can be stored in the bucket.
|
|
9
|
+
*/
|
|
10
|
+
capacity: number;
|
|
11
|
+
/**
|
|
12
|
+
* The rate, in seconds, at which tokens are refilled.
|
|
13
|
+
*/
|
|
14
|
+
refillRate: number;
|
|
17
15
|
}
|
|
16
|
+
//# sourceMappingURL=token-bucket.options.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type TokenBucketState } from './token-bucket.state.js';
|
|
2
|
+
import { type TokenBucketStatus } from './token-bucket.status.js';
|
|
3
|
+
import { type RateLimitPolicy, type RateLimitPolicyResult } from '../../core/rate-limit-policy.js';
|
|
4
|
+
/** @internal */
|
|
5
|
+
export declare class TokenBucketPolicy implements RateLimitPolicy<TokenBucketState, TokenBucketStatus> {
|
|
6
|
+
private readonly _capacity;
|
|
7
|
+
private readonly _refillRate;
|
|
8
|
+
private readonly _maxDebt;
|
|
9
|
+
constructor(_capacity: number, _refillRate: number, _maxDebt?: number);
|
|
10
|
+
get capacity(): number;
|
|
11
|
+
get refillRate(): number;
|
|
12
|
+
getInitialState(): TokenBucketState;
|
|
13
|
+
getStatus(state: TokenBucketState, now: number): TokenBucketStatus;
|
|
14
|
+
evaluate(state: TokenBucketState, now: number, cost: number, shouldReserve?: boolean): RateLimitPolicyResult<TokenBucketState>;
|
|
15
|
+
revert(state: TokenBucketState, cost: number, now: number): TokenBucketState;
|
|
16
|
+
private _deny;
|
|
17
|
+
private _syncState;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=token-bucket.policy.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { validateCost } from '../../utils/validate-cost.js';
|
|
2
|
+
/** @internal */
|
|
3
|
+
export class TokenBucketPolicy {
|
|
4
|
+
_capacity;
|
|
5
|
+
_refillRate;
|
|
6
|
+
_maxDebt;
|
|
7
|
+
constructor(_capacity, _refillRate, _maxDebt = Number.POSITIVE_INFINITY) {
|
|
8
|
+
this._capacity = _capacity;
|
|
9
|
+
this._refillRate = _refillRate;
|
|
10
|
+
this._maxDebt = _maxDebt;
|
|
11
|
+
if (!Number.isSafeInteger(_capacity) || _capacity <= 0) {
|
|
12
|
+
throw new Error(`Invalid capacity: ${_capacity}. Must be a positive integer.`);
|
|
13
|
+
}
|
|
14
|
+
if (!Number.isFinite(_refillRate) || _refillRate <= 0) {
|
|
15
|
+
throw new Error(`Invalid refillRate: ${_refillRate}. Must be a positive number.`);
|
|
16
|
+
}
|
|
17
|
+
if (_maxDebt < 0 || (!Number.isSafeInteger(_maxDebt) && _maxDebt !== Number.POSITIVE_INFINITY)) {
|
|
18
|
+
throw new Error(`Invalid maxDebt: ${_maxDebt}. Must be a non-negative integer or Infinity.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
get capacity() {
|
|
22
|
+
return this._capacity;
|
|
23
|
+
}
|
|
24
|
+
get refillRate() {
|
|
25
|
+
return this._refillRate;
|
|
26
|
+
}
|
|
27
|
+
getInitialState() {
|
|
28
|
+
return { tokens: this._capacity, debt: 0, lastRefill: 0 };
|
|
29
|
+
}
|
|
30
|
+
getStatus(state, now) {
|
|
31
|
+
const { tokens, debt } = this._syncState(state, now);
|
|
32
|
+
const deficitToOne = Math.max(0, debt + 1 - tokens);
|
|
33
|
+
const nextAvailableAt = deficitToOne === 0 ? now : now + Math.ceil((deficitToOne / this._refillRate) * 1000);
|
|
34
|
+
const deficitToCapacity = debt + (this._capacity - tokens);
|
|
35
|
+
const resetAt = deficitToCapacity === 0 ? now : now + Math.ceil((deficitToCapacity / this._refillRate) * 1000);
|
|
36
|
+
return {
|
|
37
|
+
capacity: this._capacity,
|
|
38
|
+
refillRate: this._refillRate,
|
|
39
|
+
tokens,
|
|
40
|
+
debt,
|
|
41
|
+
nextAvailableAt,
|
|
42
|
+
resetAt,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
evaluate(state, now, cost, shouldReserve) {
|
|
46
|
+
validateCost(cost, this._capacity);
|
|
47
|
+
const { tokens, debt, lastRefill } = this._syncState(state, now);
|
|
48
|
+
if (tokens >= cost) {
|
|
49
|
+
return {
|
|
50
|
+
decision: { kind: 'allow' },
|
|
51
|
+
nextState: { tokens: tokens - cost, debt, lastRefill },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const deficit = cost - tokens;
|
|
55
|
+
if (shouldReserve) {
|
|
56
|
+
const newDebt = debt + deficit;
|
|
57
|
+
if (newDebt > this._maxDebt) {
|
|
58
|
+
const targetDebt = this._maxDebt - deficit;
|
|
59
|
+
const debtToClear = Math.max(0, debt - targetDebt);
|
|
60
|
+
const waitMs = Math.ceil((debtToClear / this._refillRate) * 1000);
|
|
61
|
+
const retryAt = now + waitMs;
|
|
62
|
+
return this._deny(tokens, debt, lastRefill, retryAt);
|
|
63
|
+
}
|
|
64
|
+
const waitMs = Math.ceil((newDebt / this._refillRate) * 1000);
|
|
65
|
+
const runAt = now + waitMs;
|
|
66
|
+
return {
|
|
67
|
+
decision: { kind: 'delay', runAt },
|
|
68
|
+
nextState: { tokens: 0, debt: newDebt, lastRefill },
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const totalNeeded = debt + deficit;
|
|
72
|
+
const waitMs = Math.ceil((totalNeeded / this._refillRate) * 1000);
|
|
73
|
+
const retryAt = now + waitMs;
|
|
74
|
+
return this._deny(tokens, debt, lastRefill, retryAt);
|
|
75
|
+
}
|
|
76
|
+
revert(state, cost, now) {
|
|
77
|
+
let { tokens, debt, lastRefill } = this._syncState(state, now);
|
|
78
|
+
if (debt >= cost) {
|
|
79
|
+
debt -= cost;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const remainder = cost - debt;
|
|
83
|
+
debt = 0;
|
|
84
|
+
tokens = Math.min(this._capacity, tokens + remainder);
|
|
85
|
+
}
|
|
86
|
+
return { tokens, debt, lastRefill };
|
|
87
|
+
}
|
|
88
|
+
_deny(tokens, debt, lastRefill, retryAt) {
|
|
89
|
+
return {
|
|
90
|
+
decision: { kind: 'deny', retryAt },
|
|
91
|
+
nextState: { tokens, debt, lastRefill },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
_syncState(state, now) {
|
|
95
|
+
if (state.lastRefill === 0) {
|
|
96
|
+
return { tokens: this._capacity, debt: 0, lastRefill: now };
|
|
97
|
+
}
|
|
98
|
+
if (now <= state.lastRefill) {
|
|
99
|
+
return state;
|
|
100
|
+
}
|
|
101
|
+
let { tokens, debt } = state;
|
|
102
|
+
const elapsedMs = now - state.lastRefill;
|
|
103
|
+
const generatedTokens = (elapsedMs / 1000) * this._refillRate;
|
|
104
|
+
if (debt > 0) {
|
|
105
|
+
const payOff = Math.min(debt, generatedTokens);
|
|
106
|
+
debt -= payOff;
|
|
107
|
+
const remainingTokens = generatedTokens - payOff;
|
|
108
|
+
tokens = Math.min(this._capacity, tokens + remainingTokens);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
tokens = Math.min(this._capacity, tokens + generatedTokens);
|
|
112
|
+
}
|
|
113
|
+
return { tokens, debt, lastRefill: now };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=token-bucket.policy.js.map
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* When using a distributed state store, make sure it properly serializes and deserializes the state.
|
|
5
5
|
*/
|
|
6
6
|
export interface TokenBucketState {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
tokens: number;
|
|
8
|
+
debt: number;
|
|
9
|
+
lastRefill: number;
|
|
10
10
|
}
|
|
11
|
+
//# sourceMappingURL=token-bucket.state.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type RateLimiterStatus } from '../../core/rate-limiter-status.js';
|
|
2
|
+
/**
|
|
3
|
+
* The status of the Token Bucket rate limiter.
|
|
4
|
+
*/
|
|
5
|
+
export interface TokenBucketStatus extends RateLimiterStatus {
|
|
6
|
+
/**
|
|
7
|
+
* The maximum number of tokens that can be stored in the bucket.
|
|
8
|
+
*/
|
|
9
|
+
capacity: number;
|
|
10
|
+
/**
|
|
11
|
+
* The rate, in seconds, at which tokens are refilled.
|
|
12
|
+
*/
|
|
13
|
+
refillRate: number;
|
|
14
|
+
/**
|
|
15
|
+
* The number of tokens available in the bucket.
|
|
16
|
+
*/
|
|
17
|
+
tokens: number;
|
|
18
|
+
/**
|
|
19
|
+
* The number of tokens that have been reserved.
|
|
20
|
+
*/
|
|
21
|
+
debt: number;
|
|
22
|
+
/**
|
|
23
|
+
* The timestamp (in milliseconds) at which next token will be available for use.
|
|
24
|
+
*/
|
|
25
|
+
nextAvailableAt: number;
|
|
26
|
+
/**
|
|
27
|
+
* The timestamp (in milliseconds) at which the bucket will be reset.
|
|
28
|
+
*/
|
|
29
|
+
resetAt: number;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=token-bucket.status.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export declare class ExecutionTickets {
|
|
3
|
+
private readonly _tickets;
|
|
4
|
+
get size(): number;
|
|
5
|
+
get isEmpty(): boolean;
|
|
6
|
+
add(tick: number): void;
|
|
7
|
+
peek(): number | undefined;
|
|
8
|
+
consume(): number | undefined;
|
|
9
|
+
dropLast(): number | undefined;
|
|
10
|
+
clear(): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=execution-tickets.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Deque } from '@stimulcross/ds-deque';
|
|
2
|
+
/** @internal */
|
|
3
|
+
export class ExecutionTickets {
|
|
4
|
+
_tickets = new Deque();
|
|
5
|
+
get size() {
|
|
6
|
+
return this._tickets.size;
|
|
7
|
+
}
|
|
8
|
+
get isEmpty() {
|
|
9
|
+
return this._tickets.size === 0;
|
|
10
|
+
}
|
|
11
|
+
add(tick) {
|
|
12
|
+
this._tickets.push(tick);
|
|
13
|
+
}
|
|
14
|
+
peek() {
|
|
15
|
+
return this._tickets.peekHead();
|
|
16
|
+
}
|
|
17
|
+
consume() {
|
|
18
|
+
return this._tickets.shift();
|
|
19
|
+
}
|
|
20
|
+
dropLast() {
|
|
21
|
+
return this._tickets.pop();
|
|
22
|
+
}
|
|
23
|
+
clear() {
|
|
24
|
+
this._tickets.clear();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=execution-tickets.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Clock } from '../core/clock.js';
|
|
2
|
+
import { type StateStorage } from '../core/state-storage.js';
|
|
3
|
+
/** @internal */
|
|
4
|
+
export declare class InMemoryStateStore<V> implements StateStorage<V> {
|
|
5
|
+
private readonly _clock;
|
|
6
|
+
private readonly _state;
|
|
7
|
+
private readonly _activeLocks;
|
|
8
|
+
private readonly _waitingResolvers;
|
|
9
|
+
private readonly _cleanupTimer;
|
|
10
|
+
constructor(_clock: Clock);
|
|
11
|
+
get(key: string): Promise<V | null>;
|
|
12
|
+
set(key: string, value: V, ttlMs?: number): Promise<void>;
|
|
13
|
+
delete(key: string): Promise<void>;
|
|
14
|
+
clear(): Promise<void>;
|
|
15
|
+
destroy(): Promise<void>;
|
|
16
|
+
acquireLock(key: string): Promise<void>;
|
|
17
|
+
releaseLock(key: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=in-memory-state-store.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Deque } from '@stimulcross/ds-deque';
|
|
2
|
+
import { RateLimitErrorCode } from '../enums/rate-limit-error-code.js';
|
|
3
|
+
import { RateLimitError } from '../errors/rate-limit.error.js';
|
|
4
|
+
import { promiseWithResolvers } from '../utils/promise-with-resolvers.js';
|
|
5
|
+
const CLEANUP_INTERVAL_MS = 60_000;
|
|
6
|
+
/** @internal */
|
|
7
|
+
export class InMemoryStateStore {
|
|
8
|
+
_clock;
|
|
9
|
+
_state = new Map();
|
|
10
|
+
_activeLocks = new Set();
|
|
11
|
+
_waitingResolvers = new Map();
|
|
12
|
+
_cleanupTimer;
|
|
13
|
+
constructor(_clock) {
|
|
14
|
+
this._clock = _clock;
|
|
15
|
+
this._cleanupTimer = setInterval(() => {
|
|
16
|
+
const now = this._clock.now();
|
|
17
|
+
for (const [key, entry] of this._state.entries()) {
|
|
18
|
+
if (entry.expiresAt && entry.expiresAt <= now) {
|
|
19
|
+
this._state.delete(key);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}, CLEANUP_INTERVAL_MS);
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
24
|
+
if (this._cleanupTimer.unref) {
|
|
25
|
+
this._cleanupTimer.unref();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async get(key) {
|
|
29
|
+
const entry = this._state.get(key);
|
|
30
|
+
if (entry?.expiresAt && entry.expiresAt <= this._clock.now()) {
|
|
31
|
+
this._state.delete(key);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return entry?.value ?? null;
|
|
35
|
+
}
|
|
36
|
+
async set(key, value, ttlMs) {
|
|
37
|
+
const entry = { value };
|
|
38
|
+
if (typeof ttlMs === 'number') {
|
|
39
|
+
if (ttlMs <= 0) {
|
|
40
|
+
this._state.delete(key);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
entry.expiresAt = this._clock.now() + ttlMs;
|
|
44
|
+
}
|
|
45
|
+
this._state.set(key, entry);
|
|
46
|
+
}
|
|
47
|
+
async delete(key) {
|
|
48
|
+
this._state.delete(key);
|
|
49
|
+
}
|
|
50
|
+
async clear() {
|
|
51
|
+
this._state.clear();
|
|
52
|
+
const error = new RateLimitError(RateLimitErrorCode.Destroyed, undefined, 'Store was cleared or destroyed while waiting for lock');
|
|
53
|
+
for (const queue of this._waitingResolvers.values()) {
|
|
54
|
+
while (queue.size > 0) {
|
|
55
|
+
const waiter = queue.shift();
|
|
56
|
+
if (waiter) {
|
|
57
|
+
waiter.reject(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this._waitingResolvers.clear();
|
|
62
|
+
this._activeLocks.clear();
|
|
63
|
+
}
|
|
64
|
+
async destroy() {
|
|
65
|
+
clearInterval(this._cleanupTimer);
|
|
66
|
+
await this.clear();
|
|
67
|
+
}
|
|
68
|
+
async acquireLock(key) {
|
|
69
|
+
if (this._activeLocks.has(key)) {
|
|
70
|
+
const { promise, resolve, reject } = promiseWithResolvers();
|
|
71
|
+
const queue = this._waitingResolvers.get(key) ?? new Deque();
|
|
72
|
+
queue.push({ resolve, reject });
|
|
73
|
+
this._waitingResolvers.set(key, queue);
|
|
74
|
+
await promise;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
this._activeLocks.add(key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async releaseLock(key) {
|
|
81
|
+
const queue = this._waitingResolvers.get(key);
|
|
82
|
+
if (queue && queue.size > 0) {
|
|
83
|
+
const nextWaiter = queue.shift();
|
|
84
|
+
if (nextWaiter) {
|
|
85
|
+
nextWaiter.resolve();
|
|
86
|
+
}
|
|
87
|
+
if (queue.size === 0) {
|
|
88
|
+
this._waitingResolvers.delete(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this._activeLocks.delete(key);
|
|
93
|
+
this._waitingResolvers.delete(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=in-memory-state-store.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type SelectionPolicy } from '@stimulcross/ds-policy-priority-queue';
|
|
2
|
+
import { type Logger } from '@stimulcross/logger';
|
|
3
|
+
import { type Clock } from '../core/clock.js';
|
|
4
|
+
/** @internal */
|
|
5
|
+
export interface RateLimiterExecutionOptions {
|
|
6
|
+
id: string;
|
|
7
|
+
key: string;
|
|
8
|
+
expiresAt?: number;
|
|
9
|
+
priority?: number;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
/** @internal */
|
|
13
|
+
export interface RateLimiterExecutorQueueOptions {
|
|
14
|
+
concurrency?: number;
|
|
15
|
+
capacity?: number;
|
|
16
|
+
selectionPolicy?: SelectionPolicy;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
/** @internal */
|
|
20
|
+
export declare class RateLimiterExecutor {
|
|
21
|
+
private readonly _logger;
|
|
22
|
+
private readonly _clock;
|
|
23
|
+
private readonly _tickets;
|
|
24
|
+
private readonly _semaphore;
|
|
25
|
+
private readonly _queue;
|
|
26
|
+
private readonly _expiryHeap;
|
|
27
|
+
private _drainTimer;
|
|
28
|
+
private _expiryTimer;
|
|
29
|
+
private _nextExpiryScheduledAt;
|
|
30
|
+
constructor(_logger: Logger, clock: Clock, { concurrency, capacity, selectionPolicy }?: RateLimiterExecutorQueueOptions);
|
|
31
|
+
get isQueueFull(): boolean;
|
|
32
|
+
get queueSize(): number;
|
|
33
|
+
get queueCapacity(): number;
|
|
34
|
+
execute<T>(fn: () => T | Promise<T>, runAt: number, options: RateLimiterExecutionOptions): Promise<T>;
|
|
35
|
+
clear(): void;
|
|
36
|
+
private get _shouldPrintDebug();
|
|
37
|
+
private _drain;
|
|
38
|
+
private _getNextExpiryTimestamp;
|
|
39
|
+
private _extractExpiredTasks;
|
|
40
|
+
private _drainRemainingTasks;
|
|
41
|
+
private _scheduleDrainTimer;
|
|
42
|
+
private _recalibrateExpiryTimer;
|
|
43
|
+
private _clearDrainTimer;
|
|
44
|
+
private _clearExpiryTimer;
|
|
45
|
+
private _getStateDebugString;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=rate-limiter.executor.d.ts.map
|