@commandkit/ratelimit 0.0.0-dev.20260317060555
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/LICENSE +21 -0
- package/README.md +801 -0
- package/dist/api.d.ts +79 -0
- package/dist/api.js +266 -0
- package/dist/augmentation.d.ts +11 -0
- package/dist/augmentation.js +8 -0
- package/dist/configure.d.ts +28 -0
- package/dist/configure.js +85 -0
- package/dist/constants.d.ts +17 -0
- package/dist/constants.js +21 -0
- package/dist/directive/use-ratelimit-directive.d.ts +22 -0
- package/dist/directive/use-ratelimit-directive.js +38 -0
- package/dist/directive/use-ratelimit.d.ts +14 -0
- package/dist/directive/use-ratelimit.js +169 -0
- package/dist/engine/RateLimitEngine.d.ts +48 -0
- package/dist/engine/RateLimitEngine.js +137 -0
- package/dist/engine/algorithms/fixed-window.d.ts +44 -0
- package/dist/engine/algorithms/fixed-window.js +198 -0
- package/dist/engine/algorithms/leaky-bucket.d.ts +48 -0
- package/dist/engine/algorithms/leaky-bucket.js +119 -0
- package/dist/engine/algorithms/sliding-window.d.ts +45 -0
- package/dist/engine/algorithms/sliding-window.js +127 -0
- package/dist/engine/algorithms/token-bucket.d.ts +47 -0
- package/dist/engine/algorithms/token-bucket.js +118 -0
- package/dist/engine/violations.d.ts +55 -0
- package/dist/engine/violations.js +106 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +28 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +53 -0
- package/dist/plugin.d.ts +140 -0
- package/dist/plugin.js +796 -0
- package/dist/providers/fallback.d.ts +7 -0
- package/dist/providers/fallback.js +11 -0
- package/dist/providers/memory.d.ts +6 -0
- package/dist/providers/memory.js +11 -0
- package/dist/providers/redis.d.ts +7 -0
- package/dist/providers/redis.js +11 -0
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.js +67 -0
- package/dist/storage/fallback.d.ts +180 -0
- package/dist/storage/fallback.js +261 -0
- package/dist/storage/memory.d.ts +146 -0
- package/dist/storage/memory.js +304 -0
- package/dist/storage/redis.d.ts +130 -0
- package/dist/storage/redis.js +243 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.js +40 -0
- package/dist/utils/config.d.ts +34 -0
- package/dist/utils/config.js +105 -0
- package/dist/utils/keys.d.ts +102 -0
- package/dist/utils/keys.js +304 -0
- package/dist/utils/locking.d.ts +17 -0
- package/dist/utils/locking.js +60 -0
- package/dist/utils/time.d.ts +23 -0
- package/dist/utils/time.js +72 -0
- package/package.json +65 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Provider re-export for fallback storage.
|
|
4
|
+
*
|
|
5
|
+
* Exposes the wrapper and its options for consumers.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.FallbackRateLimitStorage = void 0;
|
|
9
|
+
var fallback_1 = require("../storage/fallback");
|
|
10
|
+
Object.defineProperty(exports, "FallbackRateLimitStorage", { enumerable: true, get: function () { return fallback_1.FallbackRateLimitStorage; } });
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFsbGJhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmlkZXJzL2ZhbGxiYWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7OztHQUlHOzs7QUFFSCxnREFBK0Q7QUFBdEQsb0hBQUEsd0JBQXdCLE9BQUEifQ==
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Provider re-export for memory storage.
|
|
4
|
+
*
|
|
5
|
+
* Keeps public imports stable across plugin packages.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.MemoryRateLimitStorage = void 0;
|
|
9
|
+
var memory_1 = require("../storage/memory");
|
|
10
|
+
Object.defineProperty(exports, "MemoryRateLimitStorage", { enumerable: true, get: function () { return memory_1.MemoryRateLimitStorage; } });
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVtb3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Byb3ZpZGVycy9tZW1vcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7OztBQUVILDRDQUEyRDtBQUFsRCxnSEFBQSxzQkFBc0IsT0FBQSJ9
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Provider re-export for Redis storage.
|
|
4
|
+
*
|
|
5
|
+
* Exposes the storage class and RedisOptions type for consumers.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.RedisRateLimitStorage = void 0;
|
|
9
|
+
var redis_1 = require("../storage/redis");
|
|
10
|
+
Object.defineProperty(exports, "RedisRateLimitStorage", { enumerable: true, get: function () { return redis_1.RedisRateLimitStorage; } });
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkaXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmlkZXJzL3JlZGlzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7OztHQUlHOzs7QUFFSCwwQ0FBeUQ7QUFBaEQsOEdBQUEscUJBQXFCLE9BQUEifQ==
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime globals for rate limiting.
|
|
3
|
+
*
|
|
4
|
+
* Stores the active storage and plugin context for directives and helpers.
|
|
5
|
+
*/
|
|
6
|
+
import type { RateLimitRuntimeContext, RateLimitStorage } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Set the default rate limit storage instance for the process.
|
|
9
|
+
*
|
|
10
|
+
* @param storage - Storage driver to use for rate-limit state.
|
|
11
|
+
* @returns Nothing; updates the process-wide default storage.
|
|
12
|
+
*/
|
|
13
|
+
export declare function setRateLimitStorage(storage: RateLimitStorage): void;
|
|
14
|
+
/**
|
|
15
|
+
* Get the default rate limit storage instance for the process.
|
|
16
|
+
*
|
|
17
|
+
* @returns Default storage instance or null if unset.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getRateLimitStorage(): RateLimitStorage | null;
|
|
20
|
+
/**
|
|
21
|
+
* Alias for setRateLimitStorage to match other packages (tasks/queue).
|
|
22
|
+
*
|
|
23
|
+
* @param storage - Storage driver to use for rate-limit state.
|
|
24
|
+
* @returns Nothing; updates the process-wide default storage.
|
|
25
|
+
*/
|
|
26
|
+
export declare function setDriver(storage: RateLimitStorage): void;
|
|
27
|
+
/**
|
|
28
|
+
* Alias for getRateLimitStorage to match other packages (tasks/queue).
|
|
29
|
+
*
|
|
30
|
+
* @returns Default storage instance or null if unset.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getDriver(): RateLimitStorage | null;
|
|
33
|
+
/**
|
|
34
|
+
* Set the active runtime context used by directives and APIs.
|
|
35
|
+
*
|
|
36
|
+
* @param runtime - Active runtime context or null to clear.
|
|
37
|
+
* @returns Nothing; updates the active runtime context.
|
|
38
|
+
*/
|
|
39
|
+
export declare function setRateLimitRuntime(runtime: RateLimitRuntimeContext | null): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get the active runtime context for directives and APIs.
|
|
42
|
+
*
|
|
43
|
+
* @returns Active runtime context or null if not initialized.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getRateLimitRuntime(): RateLimitRuntimeContext | null;
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime globals for rate limiting.
|
|
4
|
+
*
|
|
5
|
+
* Stores the active storage and plugin context for directives and helpers.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.setRateLimitStorage = setRateLimitStorage;
|
|
9
|
+
exports.getRateLimitStorage = getRateLimitStorage;
|
|
10
|
+
exports.setDriver = setDriver;
|
|
11
|
+
exports.getDriver = getDriver;
|
|
12
|
+
exports.setRateLimitRuntime = setRateLimitRuntime;
|
|
13
|
+
exports.getRateLimitRuntime = getRateLimitRuntime;
|
|
14
|
+
let defaultStorage = null;
|
|
15
|
+
let activeRuntime = null;
|
|
16
|
+
/**
|
|
17
|
+
* Set the default rate limit storage instance for the process.
|
|
18
|
+
*
|
|
19
|
+
* @param storage - Storage driver to use for rate-limit state.
|
|
20
|
+
* @returns Nothing; updates the process-wide default storage.
|
|
21
|
+
*/
|
|
22
|
+
function setRateLimitStorage(storage) {
|
|
23
|
+
defaultStorage = storage;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the default rate limit storage instance for the process.
|
|
27
|
+
*
|
|
28
|
+
* @returns Default storage instance or null if unset.
|
|
29
|
+
*/
|
|
30
|
+
function getRateLimitStorage() {
|
|
31
|
+
return defaultStorage;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Alias for setRateLimitStorage to match other packages (tasks/queue).
|
|
35
|
+
*
|
|
36
|
+
* @param storage - Storage driver to use for rate-limit state.
|
|
37
|
+
* @returns Nothing; updates the process-wide default storage.
|
|
38
|
+
*/
|
|
39
|
+
function setDriver(storage) {
|
|
40
|
+
setRateLimitStorage(storage);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Alias for getRateLimitStorage to match other packages (tasks/queue).
|
|
44
|
+
*
|
|
45
|
+
* @returns Default storage instance or null if unset.
|
|
46
|
+
*/
|
|
47
|
+
function getDriver() {
|
|
48
|
+
return getRateLimitStorage();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Set the active runtime context used by directives and APIs.
|
|
52
|
+
*
|
|
53
|
+
* @param runtime - Active runtime context or null to clear.
|
|
54
|
+
* @returns Nothing; updates the active runtime context.
|
|
55
|
+
*/
|
|
56
|
+
function setRateLimitRuntime(runtime) {
|
|
57
|
+
activeRuntime = runtime;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the active runtime context for directives and APIs.
|
|
61
|
+
*
|
|
62
|
+
* @returns Active runtime context or null if not initialized.
|
|
63
|
+
*/
|
|
64
|
+
function getRateLimitRuntime() {
|
|
65
|
+
return activeRuntime;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVudGltZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ydW50aW1lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7OztHQUlHOztBQWFILGtEQUVDO0FBT0Qsa0RBRUM7QUFRRCw4QkFFQztBQU9ELDhCQUVDO0FBUUQsa0RBSUM7QUFPRCxrREFFQztBQTVERCxJQUFJLGNBQWMsR0FBNEIsSUFBSSxDQUFDO0FBQ25ELElBQUksYUFBYSxHQUFtQyxJQUFJLENBQUM7QUFFekQ7Ozs7O0dBS0c7QUFDSCxTQUFnQixtQkFBbUIsQ0FBQyxPQUF5QjtJQUMzRCxjQUFjLEdBQUcsT0FBTyxDQUFDO0FBQzNCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBZ0IsbUJBQW1CO0lBQ2pDLE9BQU8sY0FBYyxDQUFDO0FBQ3hCLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQWdCLFNBQVMsQ0FBQyxPQUF5QjtJQUNqRCxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUMvQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQWdCLFNBQVM7SUFDdkIsT0FBTyxtQkFBbUIsRUFBRSxDQUFDO0FBQy9CLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQWdCLG1CQUFtQixDQUNqQyxPQUF1QztJQUV2QyxhQUFhLEdBQUcsT0FBTyxDQUFDO0FBQzFCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBZ0IsbUJBQW1CO0lBQ2pDLE9BQU8sYUFBYSxDQUFDO0FBQ3ZCLENBQUMifQ==
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback storage wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Routes storage calls to a secondary backend when the primary fails.
|
|
5
|
+
*/
|
|
6
|
+
import type { RateLimitStorage } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Options that control fallback logging/cooldown behavior.
|
|
9
|
+
*/
|
|
10
|
+
export interface FallbackRateLimitStorageOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Minimum time between fallback log entries (to avoid log spam).
|
|
13
|
+
*
|
|
14
|
+
* @default 30000
|
|
15
|
+
*/
|
|
16
|
+
cooldownMs?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Storage wrapper that falls back to a secondary implementation on failure.
|
|
20
|
+
*
|
|
21
|
+
* @implements RateLimitStorage
|
|
22
|
+
*/
|
|
23
|
+
export declare class FallbackRateLimitStorage implements RateLimitStorage {
|
|
24
|
+
private readonly primary;
|
|
25
|
+
private readonly secondary;
|
|
26
|
+
private readonly options;
|
|
27
|
+
private lastErrorAt;
|
|
28
|
+
/**
|
|
29
|
+
* Create a fallback wrapper with primary/secondary storages.
|
|
30
|
+
*
|
|
31
|
+
* @param primary - Primary storage backend.
|
|
32
|
+
* @param secondary - Secondary storage backend used on failure.
|
|
33
|
+
* @param options - Fallback logging and cooldown options.
|
|
34
|
+
*/
|
|
35
|
+
constructor(primary: RateLimitStorage, secondary: RateLimitStorage, options?: FallbackRateLimitStorageOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Check whether a fallback error should be logged.
|
|
38
|
+
*
|
|
39
|
+
* @returns True when the log cooldown has elapsed.
|
|
40
|
+
*/
|
|
41
|
+
private shouldLog;
|
|
42
|
+
/**
|
|
43
|
+
* Execute a storage operation with a fallback on failure.
|
|
44
|
+
*
|
|
45
|
+
* @param op - Primary operation.
|
|
46
|
+
* @param fallback - Secondary operation when primary fails.
|
|
47
|
+
* @returns Result from the primary or fallback operation.
|
|
48
|
+
*/
|
|
49
|
+
private withFallback;
|
|
50
|
+
/**
|
|
51
|
+
* Read a value using primary storage with fallback.
|
|
52
|
+
*
|
|
53
|
+
* @param key - Storage key to read.
|
|
54
|
+
* @returns Stored value or null when absent.
|
|
55
|
+
*/
|
|
56
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Store a value using primary storage with fallback.
|
|
59
|
+
*
|
|
60
|
+
* @param key - Storage key to write.
|
|
61
|
+
* @param value - Value to store.
|
|
62
|
+
* @param ttlMs - Optional TTL in milliseconds.
|
|
63
|
+
* @returns Resolves when the value is stored.
|
|
64
|
+
*/
|
|
65
|
+
set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Delete a key using primary storage with fallback.
|
|
68
|
+
*
|
|
69
|
+
* @param key - Storage key to delete.
|
|
70
|
+
* @returns Resolves when the key is removed.
|
|
71
|
+
*/
|
|
72
|
+
delete(key: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Increment a fixed-window counter using primary storage with fallback.
|
|
75
|
+
*
|
|
76
|
+
* @param key - Storage key to increment.
|
|
77
|
+
* @param ttlMs - TTL window in milliseconds.
|
|
78
|
+
* @returns Fixed-window consume result.
|
|
79
|
+
* @throws Error when either storage lacks incr support.
|
|
80
|
+
*/
|
|
81
|
+
incr(key: string, ttlMs: number): Promise<import("../types").FixedWindowConsumeResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Read TTL using primary storage with fallback.
|
|
84
|
+
*
|
|
85
|
+
* @param key - Storage key to inspect.
|
|
86
|
+
* @returns Remaining TTL in ms or null when no TTL is set.
|
|
87
|
+
* @throws Error when either storage lacks ttl support.
|
|
88
|
+
*/
|
|
89
|
+
ttl(key: string): Promise<number | null>;
|
|
90
|
+
/**
|
|
91
|
+
* Update TTL using primary storage with fallback.
|
|
92
|
+
*
|
|
93
|
+
* @param key - Storage key to update.
|
|
94
|
+
* @param ttlMs - TTL in milliseconds.
|
|
95
|
+
* @returns Resolves after the TTL is updated.
|
|
96
|
+
* @throws Error when either storage lacks expire support.
|
|
97
|
+
*/
|
|
98
|
+
expire(key: string, ttlMs: number): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Add a member to a sorted set using primary storage with fallback.
|
|
101
|
+
*
|
|
102
|
+
* @param key - Sorted-set key.
|
|
103
|
+
* @param score - Score to associate with the member.
|
|
104
|
+
* @param member - Member identifier.
|
|
105
|
+
* @returns Resolves when the member is added.
|
|
106
|
+
*/
|
|
107
|
+
zAdd(key: string, score: number, member: string): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Remove sorted-set members in a score range with fallback.
|
|
110
|
+
*
|
|
111
|
+
* @param key - Sorted-set key.
|
|
112
|
+
* @param min - Minimum score (inclusive).
|
|
113
|
+
* @param max - Maximum score (inclusive).
|
|
114
|
+
* @returns Resolves when the range is removed.
|
|
115
|
+
*/
|
|
116
|
+
zRemRangeByScore(key: string, min: number, max: number): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Count sorted-set members with fallback.
|
|
119
|
+
*
|
|
120
|
+
* @param key - Sorted-set key.
|
|
121
|
+
* @returns Number of members in the set.
|
|
122
|
+
*/
|
|
123
|
+
zCard(key: string): Promise<number>;
|
|
124
|
+
/**
|
|
125
|
+
* Read sorted-set members in a score range with fallback.
|
|
126
|
+
*
|
|
127
|
+
* @param key - Sorted-set key.
|
|
128
|
+
* @param min - Minimum score (inclusive).
|
|
129
|
+
* @param max - Maximum score (inclusive).
|
|
130
|
+
* @returns Ordered members in the score range.
|
|
131
|
+
*/
|
|
132
|
+
zRangeByScore(key: string, min: number, max: number): Promise<string[]>;
|
|
133
|
+
/**
|
|
134
|
+
* Atomically consume a fixed-window counter with fallback.
|
|
135
|
+
*
|
|
136
|
+
* @param key - Storage key to consume.
|
|
137
|
+
* @param limit - Request limit for the window.
|
|
138
|
+
* @param windowMs - Window size in milliseconds.
|
|
139
|
+
* @param nowMs - Current timestamp in milliseconds.
|
|
140
|
+
* @returns Fixed-window consume result.
|
|
141
|
+
* @throws Error when either storage lacks consumeFixedWindow support.
|
|
142
|
+
*/
|
|
143
|
+
consumeFixedWindow(key: string, limit: number, windowMs: number, nowMs: number): Promise<import("../types").FixedWindowConsumeResult>;
|
|
144
|
+
/**
|
|
145
|
+
* Atomically consume a sliding-window log with fallback.
|
|
146
|
+
*
|
|
147
|
+
* @param key - Storage key to consume.
|
|
148
|
+
* @param limit - Request limit for the window.
|
|
149
|
+
* @param windowMs - Window size in milliseconds.
|
|
150
|
+
* @param nowMs - Current timestamp in milliseconds.
|
|
151
|
+
* @param member - Member identifier for this request.
|
|
152
|
+
* @returns Sliding-window consume result.
|
|
153
|
+
* @throws Error when either storage lacks consumeSlidingWindowLog support.
|
|
154
|
+
*/
|
|
155
|
+
consumeSlidingWindowLog(key: string, limit: number, windowMs: number, nowMs: number, member: string): Promise<import("../types").SlidingWindowConsumeResult>;
|
|
156
|
+
/**
|
|
157
|
+
* Delete keys with a prefix using primary storage with fallback.
|
|
158
|
+
*
|
|
159
|
+
* @param prefix - Prefix to match.
|
|
160
|
+
* @returns Resolves after matching keys are deleted.
|
|
161
|
+
* @throws Error when either storage lacks deleteByPrefix support.
|
|
162
|
+
*/
|
|
163
|
+
deleteByPrefix(prefix: string): Promise<void>;
|
|
164
|
+
/**
|
|
165
|
+
* Delete keys matching a pattern using primary storage with fallback.
|
|
166
|
+
*
|
|
167
|
+
* @param pattern - Glob pattern to match.
|
|
168
|
+
* @returns Resolves after matching keys are deleted.
|
|
169
|
+
* @throws Error when either storage lacks deleteByPattern support.
|
|
170
|
+
*/
|
|
171
|
+
deleteByPattern(pattern: string): Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* List keys matching a prefix using primary storage with fallback.
|
|
174
|
+
*
|
|
175
|
+
* @param prefix - Prefix to match.
|
|
176
|
+
* @returns Matching keys.
|
|
177
|
+
* @throws Error when either storage lacks keysByPrefix support.
|
|
178
|
+
*/
|
|
179
|
+
keysByPrefix(prefix: string): Promise<string[]>;
|
|
180
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fallback storage wrapper.
|
|
4
|
+
*
|
|
5
|
+
* Routes storage calls to a secondary backend when the primary fails.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.FallbackRateLimitStorage = void 0;
|
|
9
|
+
const commandkit_1 = require("commandkit");
|
|
10
|
+
/**
|
|
11
|
+
* Storage wrapper that falls back to a secondary implementation on failure.
|
|
12
|
+
*
|
|
13
|
+
* @implements RateLimitStorage
|
|
14
|
+
*/
|
|
15
|
+
class FallbackRateLimitStorage {
|
|
16
|
+
/**
|
|
17
|
+
* Create a fallback wrapper with primary/secondary storages.
|
|
18
|
+
*
|
|
19
|
+
* @param primary - Primary storage backend.
|
|
20
|
+
* @param secondary - Secondary storage backend used on failure.
|
|
21
|
+
* @param options - Fallback logging and cooldown options.
|
|
22
|
+
*/
|
|
23
|
+
constructor(primary, secondary, options = {}) {
|
|
24
|
+
this.primary = primary;
|
|
25
|
+
this.secondary = secondary;
|
|
26
|
+
this.options = options;
|
|
27
|
+
this.lastErrorAt = 0;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check whether a fallback error should be logged.
|
|
31
|
+
*
|
|
32
|
+
* @returns True when the log cooldown has elapsed.
|
|
33
|
+
*/
|
|
34
|
+
shouldLog() {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const cooldown = this.options.cooldownMs ?? 30000;
|
|
37
|
+
if (now - this.lastErrorAt > cooldown) {
|
|
38
|
+
this.lastErrorAt = now;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Execute a storage operation with a fallback on failure.
|
|
45
|
+
*
|
|
46
|
+
* @param op - Primary operation.
|
|
47
|
+
* @param fallback - Secondary operation when primary fails.
|
|
48
|
+
* @returns Result from the primary or fallback operation.
|
|
49
|
+
*/
|
|
50
|
+
async withFallback(op, fallback) {
|
|
51
|
+
try {
|
|
52
|
+
return await op();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (this.shouldLog()) {
|
|
56
|
+
commandkit_1.Logger.error `[ratelimit] Storage error, falling back to secondary: ${error}`;
|
|
57
|
+
}
|
|
58
|
+
return fallback();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read a value using primary storage with fallback.
|
|
63
|
+
*
|
|
64
|
+
* @param key - Storage key to read.
|
|
65
|
+
* @returns Stored value or null when absent.
|
|
66
|
+
*/
|
|
67
|
+
async get(key) {
|
|
68
|
+
return this.withFallback(() => this.primary.get(key), () => this.secondary.get(key));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Store a value using primary storage with fallback.
|
|
72
|
+
*
|
|
73
|
+
* @param key - Storage key to write.
|
|
74
|
+
* @param value - Value to store.
|
|
75
|
+
* @param ttlMs - Optional TTL in milliseconds.
|
|
76
|
+
* @returns Resolves when the value is stored.
|
|
77
|
+
*/
|
|
78
|
+
async set(key, value, ttlMs) {
|
|
79
|
+
return this.withFallback(() => this.primary.set(key, value, ttlMs), () => this.secondary.set(key, value, ttlMs));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Delete a key using primary storage with fallback.
|
|
83
|
+
*
|
|
84
|
+
* @param key - Storage key to delete.
|
|
85
|
+
* @returns Resolves when the key is removed.
|
|
86
|
+
*/
|
|
87
|
+
async delete(key) {
|
|
88
|
+
return this.withFallback(() => this.primary.delete(key), () => this.secondary.delete(key));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Increment a fixed-window counter using primary storage with fallback.
|
|
92
|
+
*
|
|
93
|
+
* @param key - Storage key to increment.
|
|
94
|
+
* @param ttlMs - TTL window in milliseconds.
|
|
95
|
+
* @returns Fixed-window consume result.
|
|
96
|
+
* @throws Error when either storage lacks incr support.
|
|
97
|
+
*/
|
|
98
|
+
async incr(key, ttlMs) {
|
|
99
|
+
if (!this.primary.incr || !this.secondary.incr) {
|
|
100
|
+
throw new Error('incr not supported by both storages');
|
|
101
|
+
}
|
|
102
|
+
return this.withFallback(() => this.primary.incr(key, ttlMs), () => this.secondary.incr(key, ttlMs));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Read TTL using primary storage with fallback.
|
|
106
|
+
*
|
|
107
|
+
* @param key - Storage key to inspect.
|
|
108
|
+
* @returns Remaining TTL in ms or null when no TTL is set.
|
|
109
|
+
* @throws Error when either storage lacks ttl support.
|
|
110
|
+
*/
|
|
111
|
+
async ttl(key) {
|
|
112
|
+
if (!this.primary.ttl || !this.secondary.ttl) {
|
|
113
|
+
throw new Error('ttl not supported by both storages');
|
|
114
|
+
}
|
|
115
|
+
return this.withFallback(() => this.primary.ttl(key), () => this.secondary.ttl(key));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Update TTL using primary storage with fallback.
|
|
119
|
+
*
|
|
120
|
+
* @param key - Storage key to update.
|
|
121
|
+
* @param ttlMs - TTL in milliseconds.
|
|
122
|
+
* @returns Resolves after the TTL is updated.
|
|
123
|
+
* @throws Error when either storage lacks expire support.
|
|
124
|
+
*/
|
|
125
|
+
async expire(key, ttlMs) {
|
|
126
|
+
if (!this.primary.expire || !this.secondary.expire) {
|
|
127
|
+
throw new Error('expire not supported by both storages');
|
|
128
|
+
}
|
|
129
|
+
return this.withFallback(() => this.primary.expire(key, ttlMs), () => this.secondary.expire(key, ttlMs));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Add a member to a sorted set using primary storage with fallback.
|
|
133
|
+
*
|
|
134
|
+
* @param key - Sorted-set key.
|
|
135
|
+
* @param score - Score to associate with the member.
|
|
136
|
+
* @param member - Member identifier.
|
|
137
|
+
* @returns Resolves when the member is added.
|
|
138
|
+
*/
|
|
139
|
+
async zAdd(key, score, member) {
|
|
140
|
+
if (!this.primary.zAdd || !this.secondary.zAdd) {
|
|
141
|
+
throw new Error('zAdd not supported by both storages');
|
|
142
|
+
}
|
|
143
|
+
return this.withFallback(() => this.primary.zAdd(key, score, member), () => this.secondary.zAdd(key, score, member));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Remove sorted-set members in a score range with fallback.
|
|
147
|
+
*
|
|
148
|
+
* @param key - Sorted-set key.
|
|
149
|
+
* @param min - Minimum score (inclusive).
|
|
150
|
+
* @param max - Maximum score (inclusive).
|
|
151
|
+
* @returns Resolves when the range is removed.
|
|
152
|
+
*/
|
|
153
|
+
async zRemRangeByScore(key, min, max) {
|
|
154
|
+
if (!this.primary.zRemRangeByScore || !this.secondary.zRemRangeByScore) {
|
|
155
|
+
throw new Error('zRemRangeByScore not supported by both storages');
|
|
156
|
+
}
|
|
157
|
+
return this.withFallback(() => this.primary.zRemRangeByScore(key, min, max), () => this.secondary.zRemRangeByScore(key, min, max));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Count sorted-set members with fallback.
|
|
161
|
+
*
|
|
162
|
+
* @param key - Sorted-set key.
|
|
163
|
+
* @returns Number of members in the set.
|
|
164
|
+
*/
|
|
165
|
+
async zCard(key) {
|
|
166
|
+
if (!this.primary.zCard || !this.secondary.zCard) {
|
|
167
|
+
throw new Error('zCard not supported by both storages');
|
|
168
|
+
}
|
|
169
|
+
return this.withFallback(() => this.primary.zCard(key), () => this.secondary.zCard(key));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Read sorted-set members in a score range with fallback.
|
|
173
|
+
*
|
|
174
|
+
* @param key - Sorted-set key.
|
|
175
|
+
* @param min - Minimum score (inclusive).
|
|
176
|
+
* @param max - Maximum score (inclusive).
|
|
177
|
+
* @returns Ordered members in the score range.
|
|
178
|
+
*/
|
|
179
|
+
async zRangeByScore(key, min, max) {
|
|
180
|
+
if (!this.primary.zRangeByScore || !this.secondary.zRangeByScore) {
|
|
181
|
+
throw new Error('zRangeByScore not supported by both storages');
|
|
182
|
+
}
|
|
183
|
+
return this.withFallback(() => this.primary.zRangeByScore(key, min, max), () => this.secondary.zRangeByScore(key, min, max));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Atomically consume a fixed-window counter with fallback.
|
|
187
|
+
*
|
|
188
|
+
* @param key - Storage key to consume.
|
|
189
|
+
* @param limit - Request limit for the window.
|
|
190
|
+
* @param windowMs - Window size in milliseconds.
|
|
191
|
+
* @param nowMs - Current timestamp in milliseconds.
|
|
192
|
+
* @returns Fixed-window consume result.
|
|
193
|
+
* @throws Error when either storage lacks consumeFixedWindow support.
|
|
194
|
+
*/
|
|
195
|
+
async consumeFixedWindow(key, limit, windowMs, nowMs) {
|
|
196
|
+
if (!this.primary.consumeFixedWindow ||
|
|
197
|
+
!this.secondary.consumeFixedWindow) {
|
|
198
|
+
throw new Error('consumeFixedWindow not supported by both storages');
|
|
199
|
+
}
|
|
200
|
+
return this.withFallback(() => this.primary.consumeFixedWindow(key, limit, windowMs, nowMs), () => this.secondary.consumeFixedWindow(key, limit, windowMs, nowMs));
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Atomically consume a sliding-window log with fallback.
|
|
204
|
+
*
|
|
205
|
+
* @param key - Storage key to consume.
|
|
206
|
+
* @param limit - Request limit for the window.
|
|
207
|
+
* @param windowMs - Window size in milliseconds.
|
|
208
|
+
* @param nowMs - Current timestamp in milliseconds.
|
|
209
|
+
* @param member - Member identifier for this request.
|
|
210
|
+
* @returns Sliding-window consume result.
|
|
211
|
+
* @throws Error when either storage lacks consumeSlidingWindowLog support.
|
|
212
|
+
*/
|
|
213
|
+
async consumeSlidingWindowLog(key, limit, windowMs, nowMs, member) {
|
|
214
|
+
if (!this.primary.consumeSlidingWindowLog ||
|
|
215
|
+
!this.secondary.consumeSlidingWindowLog) {
|
|
216
|
+
throw new Error('consumeSlidingWindowLog not supported by both storages');
|
|
217
|
+
}
|
|
218
|
+
return this.withFallback(() => this.primary.consumeSlidingWindowLog(key, limit, windowMs, nowMs, member), () => this.secondary.consumeSlidingWindowLog(key, limit, windowMs, nowMs, member));
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Delete keys with a prefix using primary storage with fallback.
|
|
222
|
+
*
|
|
223
|
+
* @param prefix - Prefix to match.
|
|
224
|
+
* @returns Resolves after matching keys are deleted.
|
|
225
|
+
* @throws Error when either storage lacks deleteByPrefix support.
|
|
226
|
+
*/
|
|
227
|
+
async deleteByPrefix(prefix) {
|
|
228
|
+
if (!this.primary.deleteByPrefix || !this.secondary.deleteByPrefix) {
|
|
229
|
+
throw new Error('deleteByPrefix not supported by both storages');
|
|
230
|
+
}
|
|
231
|
+
return this.withFallback(() => this.primary.deleteByPrefix(prefix), () => this.secondary.deleteByPrefix(prefix));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Delete keys matching a pattern using primary storage with fallback.
|
|
235
|
+
*
|
|
236
|
+
* @param pattern - Glob pattern to match.
|
|
237
|
+
* @returns Resolves after matching keys are deleted.
|
|
238
|
+
* @throws Error when either storage lacks deleteByPattern support.
|
|
239
|
+
*/
|
|
240
|
+
async deleteByPattern(pattern) {
|
|
241
|
+
if (!this.primary.deleteByPattern || !this.secondary.deleteByPattern) {
|
|
242
|
+
throw new Error('deleteByPattern not supported by both storages');
|
|
243
|
+
}
|
|
244
|
+
return this.withFallback(() => this.primary.deleteByPattern(pattern), () => this.secondary.deleteByPattern(pattern));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* List keys matching a prefix using primary storage with fallback.
|
|
248
|
+
*
|
|
249
|
+
* @param prefix - Prefix to match.
|
|
250
|
+
* @returns Matching keys.
|
|
251
|
+
* @throws Error when either storage lacks keysByPrefix support.
|
|
252
|
+
*/
|
|
253
|
+
async keysByPrefix(prefix) {
|
|
254
|
+
if (!this.primary.keysByPrefix || !this.secondary.keysByPrefix) {
|
|
255
|
+
throw new Error('keysByPrefix not supported by both storages');
|
|
256
|
+
}
|
|
257
|
+
return this.withFallback(() => this.primary.keysByPrefix(prefix), () => this.secondary.keysByPrefix(prefix));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.FallbackRateLimitStorage = FallbackRateLimitStorage;
|
|
261
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFsbGJhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmFnZS9mYWxsYmFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7R0FJRzs7O0FBRUgsMkNBQW9DO0FBZXBDOzs7O0dBSUc7QUFDSCxNQUFhLHdCQUF3QjtJQUduQzs7Ozs7O09BTUc7SUFDSCxZQUNtQixPQUF5QixFQUN6QixTQUEyQixFQUMzQixVQUEyQyxFQUFFO1FBRjdDLFlBQU8sR0FBUCxPQUFPLENBQWtCO1FBQ3pCLGNBQVMsR0FBVCxTQUFTLENBQWtCO1FBQzNCLFlBQU8sR0FBUCxPQUFPLENBQXNDO1FBWnhELGdCQUFXLEdBQUcsQ0FBQyxDQUFDO0lBYXJCLENBQUM7SUFFSjs7OztPQUlHO0lBQ0ssU0FBUztRQUNmLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN2QixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsSUFBSSxLQUFNLENBQUM7UUFDbkQsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLFdBQVcsR0FBRyxRQUFRLEVBQUUsQ0FBQztZQUN0QyxJQUFJLENBQUMsV0FBVyxHQUFHLEdBQUcsQ0FBQztZQUN2QixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSyxLQUFLLENBQUMsWUFBWSxDQUN4QixFQUFvQixFQUNwQixRQUEwQjtRQUUxQixJQUFJLENBQUM7WUFDSCxPQUFPLE1BQU0sRUFBRSxFQUFFLENBQUM7UUFDcEIsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDO2dCQUNyQixtQkFBTSxDQUFDLEtBQUssQ0FBQSx5REFBeUQsS0FBSyxFQUFFLENBQUM7WUFDL0UsQ0FBQztZQUNELE9BQU8sUUFBUSxFQUFF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
|