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