@rupayan-das/request-guard 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/LICENSE +21 -0
- package/README.md +232 -0
- package/dist/config/redis.config.d.ts +3 -0
- package/dist/config/redis.config.d.ts.map +1 -0
- package/dist/config/redis.config.js +6 -0
- package/dist/config/redis.config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/embedding.interface.d.ts +4 -0
- package/dist/interfaces/embedding.interface.d.ts.map +1 -0
- package/dist/interfaces/embedding.interface.js +2 -0
- package/dist/interfaces/embedding.interface.js.map +1 -0
- package/dist/interfaces/llm.interface.d.ts +4 -0
- package/dist/interfaces/llm.interface.d.ts.map +1 -0
- package/dist/interfaces/llm.interface.js +2 -0
- package/dist/interfaces/llm.interface.js.map +1 -0
- package/dist/rate_limiter/algorithms/leakyBucket.algo.d.ts +37 -0
- package/dist/rate_limiter/algorithms/leakyBucket.algo.d.ts.map +1 -0
- package/dist/rate_limiter/algorithms/leakyBucket.algo.js +73 -0
- package/dist/rate_limiter/algorithms/leakyBucket.algo.js.map +1 -0
- package/dist/rate_limiter/algorithms/slidingWindow.algo.d.ts +13 -0
- package/dist/rate_limiter/algorithms/slidingWindow.algo.d.ts.map +1 -0
- package/dist/rate_limiter/algorithms/slidingWindow.algo.js +56 -0
- package/dist/rate_limiter/algorithms/slidingWindow.algo.js.map +1 -0
- package/dist/rate_limiter/algorithms/tokenBucket.algo.d.ts +37 -0
- package/dist/rate_limiter/algorithms/tokenBucket.algo.d.ts.map +1 -0
- package/dist/rate_limiter/algorithms/tokenBucket.algo.js +88 -0
- package/dist/rate_limiter/algorithms/tokenBucket.algo.js.map +1 -0
- package/dist/rate_limiter/rateLimiter.factory.d.ts +22 -0
- package/dist/rate_limiter/rateLimiter.factory.d.ts.map +1 -0
- package/dist/rate_limiter/rateLimiter.factory.js +15 -0
- package/dist/rate_limiter/rateLimiter.factory.js.map +1 -0
- package/dist/rate_limiter/rateLimiter.interface.d.ts +5 -0
- package/dist/rate_limiter/rateLimiter.interface.d.ts.map +1 -0
- package/dist/rate_limiter/rateLimiter.interface.js +2 -0
- package/dist/rate_limiter/rateLimiter.interface.js.map +1 -0
- package/dist/semantic_cache/config/semantic_cache.config.d.ts +2 -0
- package/dist/semantic_cache/config/semantic_cache.config.d.ts.map +1 -0
- package/dist/semantic_cache/config/semantic_cache.config.js +2 -0
- package/dist/semantic_cache/config/semantic_cache.config.js.map +1 -0
- package/dist/semantic_cache/services/semantic_cache.service.d.ts +23 -0
- package/dist/semantic_cache/services/semantic_cache.service.d.ts.map +1 -0
- package/dist/semantic_cache/services/semantic_cache.service.js +82 -0
- package/dist/semantic_cache/services/semantic_cache.service.js.map +1 -0
- package/dist/semantic_cache/utils/similarity.utils.d.ts +2 -0
- package/dist/semantic_cache/utils/similarity.utils.d.ts.map +1 -0
- package/dist/semantic_cache/utils/similarity.utils.js +18 -0
- package/dist/semantic_cache/utils/similarity.utils.js.map +1 -0
- package/dist/semantic_cache/utils/vector.utils.d.ts +2 -0
- package/dist/semantic_cache/utils/vector.utils.d.ts.map +1 -0
- package/dist/semantic_cache/utils/vector.utils.js +5 -0
- package/dist/semantic_cache/utils/vector.utils.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rupayan Das
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Request Guard
|
|
2
|
+
|
|
3
|
+
Redis-based rate limiting and semantic caching for Node.js applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* Token Bucket rate limiting
|
|
8
|
+
* Leaky Bucket rate limiting
|
|
9
|
+
* Sliding Window rate limiting
|
|
10
|
+
* Redis-backed state
|
|
11
|
+
* Semantic caching for LLM responses
|
|
12
|
+
* Pluggable LLM and embedding providers
|
|
13
|
+
* TypeScript support
|
|
14
|
+
* ESM support
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @rupayan-das/request-guard redis
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Make sure Redis is running and accessible from your application.
|
|
23
|
+
|
|
24
|
+
Semantic caching requires Redis with vector search support.
|
|
25
|
+
|
|
26
|
+
## Rate Limiting
|
|
27
|
+
|
|
28
|
+
### Using the Factory
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { createRateLimiter } from "@rupayan-das/request-guard";
|
|
32
|
+
import { createClient } from "redis";
|
|
33
|
+
|
|
34
|
+
const client = createClient({
|
|
35
|
+
url: "redis://localhost:6379",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await client.connect();
|
|
39
|
+
|
|
40
|
+
const rateLimiter = createRateLimiter({
|
|
41
|
+
algorithm: "token-bucket",
|
|
42
|
+
capacity: 10,
|
|
43
|
+
refillRate: 1,
|
|
44
|
+
client,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const allowed = await rateLimiter.allowRequest("user-123");
|
|
48
|
+
|
|
49
|
+
console.log(allowed);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`allowRequest()` returns `true` when the request is allowed and `false` when the rate limit has been exceeded.
|
|
53
|
+
|
|
54
|
+
## Available Algorithms
|
|
55
|
+
|
|
56
|
+
### Token Bucket
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const rateLimiter = createRateLimiter({
|
|
60
|
+
algorithm: "token-bucket",
|
|
61
|
+
capacity: 10,
|
|
62
|
+
refillRate: 1,
|
|
63
|
+
client,
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
* `capacity`: Maximum number of tokens in the bucket.
|
|
68
|
+
* `refillRate`: Number of tokens added per second.
|
|
69
|
+
|
|
70
|
+
### Leaky Bucket
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const rateLimiter = createRateLimiter({
|
|
74
|
+
algorithm: "leaky-bucket",
|
|
75
|
+
capacity: 10,
|
|
76
|
+
leakRate: 1,
|
|
77
|
+
client,
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
* `capacity`: Maximum bucket capacity.
|
|
82
|
+
* `leakRate`: Number of requests processed per second.
|
|
83
|
+
|
|
84
|
+
### Sliding Window
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const rateLimiter = createRateLimiter({
|
|
88
|
+
algorithm: "sliding-window",
|
|
89
|
+
windowSize: 60,
|
|
90
|
+
maxRequests: 10,
|
|
91
|
+
client,
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
* `windowSize`: Window duration in seconds.
|
|
96
|
+
* `maxRequests`: Maximum number of requests allowed within the window.
|
|
97
|
+
|
|
98
|
+
## Semantic Cache
|
|
99
|
+
|
|
100
|
+
Semantic caching uses embeddings to determine whether a new query is sufficiently similar to a previously cached query.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { SemanticCache } from "@rupayan-das/request-guard";
|
|
104
|
+
|
|
105
|
+
const cache = new SemanticCache({
|
|
106
|
+
client,
|
|
107
|
+
embeddingProvider,
|
|
108
|
+
llmProvider,
|
|
109
|
+
threshold: 0.6,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const response = await cache.getOrGenerate(
|
|
113
|
+
"Explain how Redis works"
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
console.log(response);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If a sufficiently similar query exists in the cache, its cached response is returned instead of generating a new LLM response.
|
|
120
|
+
|
|
121
|
+
The default similarity threshold is `0.6`.
|
|
122
|
+
|
|
123
|
+
A higher threshold requires queries to be more similar to produce a cache hit. A lower threshold allows more loosely related queries to use cached responses.
|
|
124
|
+
|
|
125
|
+
## Providers
|
|
126
|
+
|
|
127
|
+
The semantic cache does not depend on a specific AI provider.
|
|
128
|
+
|
|
129
|
+
You provide implementations of the `EmbeddingProvider` and `LLMProvider` interfaces.
|
|
130
|
+
|
|
131
|
+
### Embedding Provider
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
interface EmbeddingProvider {
|
|
135
|
+
generateEmbedding(text: string): Promise<number[]>;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### LLM Provider
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
interface LLMProvider {
|
|
143
|
+
askLLM(prompt: string): Promise<string>;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
This allows the semantic cache to work with different LLM and embedding providers.
|
|
148
|
+
|
|
149
|
+
For example, you can implement these interfaces using OpenAI, Anthropic, Gemini, a local model, or another provider.
|
|
150
|
+
|
|
151
|
+
## Direct Algorithm Usage
|
|
152
|
+
|
|
153
|
+
The individual rate limiting algorithms can also be instantiated directly.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { TokenBucket } from "@rupayan-das/request-guard";
|
|
157
|
+
|
|
158
|
+
const limiter = new TokenBucket(
|
|
159
|
+
10,
|
|
160
|
+
1,
|
|
161
|
+
client
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const allowed = await limiter.allowRequest("user-123");
|
|
165
|
+
|
|
166
|
+
console.log(allowed);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The same approach can be used with `LeakyBucket` and `SlidingWindow`.
|
|
170
|
+
|
|
171
|
+
## API
|
|
172
|
+
|
|
173
|
+
### `createRateLimiter(options)`
|
|
174
|
+
|
|
175
|
+
Creates a rate limiter using one of the supported algorithms:
|
|
176
|
+
|
|
177
|
+
* `token-bucket`
|
|
178
|
+
* `leaky-bucket`
|
|
179
|
+
* `sliding-window`
|
|
180
|
+
|
|
181
|
+
### `SemanticCache(options)`
|
|
182
|
+
|
|
183
|
+
Creates a semantic cache using:
|
|
184
|
+
|
|
185
|
+
* A Redis client
|
|
186
|
+
* An embedding provider
|
|
187
|
+
* An LLM provider
|
|
188
|
+
* An optional similarity threshold
|
|
189
|
+
|
|
190
|
+
### `allowRequest(userId)`
|
|
191
|
+
|
|
192
|
+
Checks whether a request should be allowed for the specified user.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
Promise<boolean>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
* `true`: Request is allowed.
|
|
201
|
+
* `false`: Rate limit has been exceeded.
|
|
202
|
+
|
|
203
|
+
### `getOrGenerate(query)`
|
|
204
|
+
|
|
205
|
+
Checks the semantic cache for a sufficiently similar query.
|
|
206
|
+
|
|
207
|
+
* Returns the cached response on a cache hit.
|
|
208
|
+
* Calls the configured LLM provider on a cache miss.
|
|
209
|
+
* Stores the generated response in the semantic cache.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
Promise<string>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Redis
|
|
218
|
+
|
|
219
|
+
The rate limiter uses Redis to store rate limiting state.
|
|
220
|
+
|
|
221
|
+
The semantic cache uses Redis to store cached queries, responses, and embeddings and to perform vector similarity searches.
|
|
222
|
+
|
|
223
|
+
Make sure your Redis instance is accessible from your Node.js application.
|
|
224
|
+
|
|
225
|
+
## Requirements
|
|
226
|
+
|
|
227
|
+
* Node.js `>= 20`
|
|
228
|
+
* Redis with vector search support for semantic caching
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
ISC
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.config.d.ts","sourceRoot":"","sources":["../../src/config/redis.config.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,MAAM,oDAAiB,CAAC;AAM9B,OAAO,EACH,MAAM,GACT,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.config.js","sourceRoot":"","sources":["../../src/config/redis.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAErC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;AAE9B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC,CAAC;AAElE,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;AAEvB,OAAO,EACH,MAAM,GACT,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { TokenBucket } from "./rate_limiter/algorithms/tokenBucket.algo.js";
|
|
2
|
+
export { LeakyBucket } from "./rate_limiter/algorithms/leakyBucket.algo.js";
|
|
3
|
+
export { SlidingWindow } from "./rate_limiter/algorithms/slidingWindow.algo.js";
|
|
4
|
+
export { createRateLimiter } from "./rate_limiter/rateLimiter.factory.js";
|
|
5
|
+
export { SemanticCache } from "./semantic_cache/services/semantic_cache.service.js";
|
|
6
|
+
export type { RateLimiter } from "./rate_limiter/rateLimiter.interface.js";
|
|
7
|
+
export type { RateLimiterOptions } from "./rate_limiter/rateLimiter.factory.js";
|
|
8
|
+
export type { LLMProvider } from "./interfaces/llm.interface.js";
|
|
9
|
+
export type { EmbeddingProvider } from "./interfaces/embedding.interface.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,+CAA+C,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,+CAA+C,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,OAAO,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAC;AAEpF,YAAY,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAC3E,YAAY,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,YAAY,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { TokenBucket } from "./rate_limiter/algorithms/tokenBucket.algo.js";
|
|
2
|
+
export { LeakyBucket } from "./rate_limiter/algorithms/leakyBucket.algo.js";
|
|
3
|
+
export { SlidingWindow } from "./rate_limiter/algorithms/slidingWindow.algo.js";
|
|
4
|
+
export { createRateLimiter } from "./rate_limiter/rateLimiter.factory.js";
|
|
5
|
+
export { SemanticCache } from "./semantic_cache/services/semantic_cache.service.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,+CAA+C,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,+CAA+C,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,OAAO,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/embedding.interface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAC9B,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACtD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.interface.js","sourceRoot":"","sources":["../../src/interfaces/embedding.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/llm.interface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IACxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.interface.js","sourceRoot":"","sources":["../../src/interfaces/llm.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import type { RateLimiter } from "../rateLimiter.interface.js";
|
|
3
|
+
declare class LeakyBucket implements RateLimiter {
|
|
4
|
+
private capacity;
|
|
5
|
+
private leakRate;
|
|
6
|
+
private client;
|
|
7
|
+
private readonly script;
|
|
8
|
+
constructor(capacity: number, leakRate: number, client: RedisClientType);
|
|
9
|
+
allowRequest(userId: string): Promise<boolean>;
|
|
10
|
+
runScript(userId: string): Promise<string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | /*elided*/ any | import("redis").BlobError | import("redis").SimpleError | {
|
|
11
|
+
[x: string]: string | number | bigint | boolean | /*elided*/ any | import("redis").BlobError | import("redis").SimpleError | /*elided*/ any | null;
|
|
12
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
13
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
14
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
15
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
16
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
17
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
18
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
19
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
20
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
21
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
22
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
23
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
24
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
25
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
26
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
27
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
28
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
29
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
30
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
31
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
32
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
33
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
34
|
+
} | null>;
|
|
35
|
+
}
|
|
36
|
+
export { LeakyBucket };
|
|
37
|
+
//# sourceMappingURL=leakyBucket.algo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leakyBucket.algo.d.ts","sourceRoot":"","sources":["../../../src/rate_limiter/algorithms/leakyBucket.algo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,cAAM,WAAY,YAAW,WAAW;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CA0CrB;IAEF,YAAY,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAItE;IAEK,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnD;IAGK,SAAS,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;cAU7B;CAGJ;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class LeakyBucket {
|
|
2
|
+
capacity;
|
|
3
|
+
leakRate;
|
|
4
|
+
client;
|
|
5
|
+
script = `
|
|
6
|
+
local capacity = tonumber(ARGV[1])
|
|
7
|
+
local leakRate = tonumber(ARGV[2])
|
|
8
|
+
local now = tonumber(ARGV[3])
|
|
9
|
+
local lastLeakTime = tonumber(redis.call("HGET", KEYS[1], "lastLeakTime"))
|
|
10
|
+
local currentLevel = tonumber(redis.call("HGET", KEYS[1], "currentLevel"))
|
|
11
|
+
|
|
12
|
+
if currentLevel == nil then
|
|
13
|
+
currentLevel = 0
|
|
14
|
+
lastLeakTime = now
|
|
15
|
+
redis.call("HSET", KEYS[1],
|
|
16
|
+
"currentLevel", currentLevel,
|
|
17
|
+
"lastLeakTime", now
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
else
|
|
21
|
+
lastLeakTime = tonumber(redis.call("HGET", KEYS[1], "lastLeakTime"))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
local elapsedTime = (now - lastLeakTime) / 1000
|
|
25
|
+
local leakedRequests = math.floor(leakRate * elapsedTime)
|
|
26
|
+
|
|
27
|
+
if leakedRequests > 0 then
|
|
28
|
+
currentLevel = currentLevel - leakedRequests
|
|
29
|
+
if (currentLevel < 0) then
|
|
30
|
+
currentLevel = 0
|
|
31
|
+
end
|
|
32
|
+
lastLeakTime = lastLeakTime + (leakedRequests / leakRate) * 1000
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
local allowed = 0
|
|
36
|
+
if (currentLevel < capacity) then
|
|
37
|
+
currentLevel = currentLevel + 1
|
|
38
|
+
allowed = 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
redis.call("HSET", KEYS[1],
|
|
42
|
+
"currentLevel", currentLevel,
|
|
43
|
+
"lastLeakTime", lastLeakTime
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return allowed
|
|
47
|
+
`;
|
|
48
|
+
constructor(capacity, leakRate, client) {
|
|
49
|
+
this.capacity = capacity;
|
|
50
|
+
this.leakRate = leakRate;
|
|
51
|
+
this.client = client;
|
|
52
|
+
}
|
|
53
|
+
async allowRequest(userId) {
|
|
54
|
+
const result = await this.runScript(userId);
|
|
55
|
+
if (result === 1) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
async runScript(userId) {
|
|
61
|
+
const result = await this.client.eval(this.script, {
|
|
62
|
+
keys: [`leaky_bucket:${userId}`],
|
|
63
|
+
arguments: [
|
|
64
|
+
String(this.capacity),
|
|
65
|
+
String(this.leakRate),
|
|
66
|
+
String(Date.now()),
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export { LeakyBucket };
|
|
73
|
+
//# sourceMappingURL=leakyBucket.algo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leakyBucket.algo.js","sourceRoot":"","sources":["../../../src/rate_limiter/algorithms/leakyBucket.algo.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW;IACL,QAAQ,CAAS;IACjB,QAAQ,CAAS;IACjB,MAAM,CAAkB;IAEf,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0CzB,CAAC;IAEF,YAAY,QAAgB,EAAE,QAAgB,EAAE,MAAuB;QACnE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAE5C,IAAG,MAAM,KAAK,CAAC,EAAC,CAAC;YACb,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAGD,KAAK,CAAC,SAAS,CAAC,MAAc;QAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAC/C,IAAI,EAAE,CAAC,gBAAgB,MAAM,EAAE,CAAC;YAChC,SAAS,EAAE;gBACP,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;aACrB;SACJ,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAClB,CAAC;CAGJ;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import type { RateLimiter } from "../rateLimiter.interface.js";
|
|
3
|
+
declare class SlidingWindow implements RateLimiter {
|
|
4
|
+
private windowSize;
|
|
5
|
+
private maxRequests;
|
|
6
|
+
private client;
|
|
7
|
+
private readonly script;
|
|
8
|
+
constructor(windowSize: number, maxRequests: number, client: RedisClientType);
|
|
9
|
+
allowRequest(userId: string): Promise<boolean>;
|
|
10
|
+
runScript(userId: string, requestId: string): Promise<number>;
|
|
11
|
+
}
|
|
12
|
+
export { SlidingWindow, };
|
|
13
|
+
//# sourceMappingURL=slidingWindow.algo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slidingWindow.algo.d.ts","sourceRoot":"","sources":["../../../src/rate_limiter/algorithms/slidingWindow.algo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,cAAM,aAAc,YAAW,WAAW;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CA0BtB;IACD,YAAY,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAI3E;IAGK,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAInD;IAGK,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWlE;CAEJ;AAED,OAAM,EACF,aAAa,GAChB,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
class SlidingWindow {
|
|
2
|
+
windowSize;
|
|
3
|
+
maxRequests;
|
|
4
|
+
client;
|
|
5
|
+
script = `
|
|
6
|
+
local now = tonumber(ARGV[1])
|
|
7
|
+
local maxRequests = tonumber(ARGV[2])
|
|
8
|
+
local windowSize = tonumber(ARGV[3])
|
|
9
|
+
local requestId = ARGV[4]
|
|
10
|
+
local windowStart = now - windowSize * 1000
|
|
11
|
+
|
|
12
|
+
redis.call(
|
|
13
|
+
"ZREMRANGEBYSCORE",
|
|
14
|
+
KEYS[1],
|
|
15
|
+
0,
|
|
16
|
+
windowStart
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
local requestCount = redis.call("ZCARD", KEYS[1])
|
|
20
|
+
if requestCount < maxRequests then
|
|
21
|
+
redis.call(
|
|
22
|
+
"ZADD",
|
|
23
|
+
KEYS[1],
|
|
24
|
+
now,
|
|
25
|
+
requestId
|
|
26
|
+
)
|
|
27
|
+
return 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
return 0
|
|
31
|
+
`;
|
|
32
|
+
constructor(windowSize, maxRequests, client) {
|
|
33
|
+
this.windowSize = windowSize;
|
|
34
|
+
this.maxRequests = maxRequests;
|
|
35
|
+
this.client = client;
|
|
36
|
+
}
|
|
37
|
+
async allowRequest(userId) {
|
|
38
|
+
const requestId = `${Date.now()}-${crypto.randomUUID()}`;
|
|
39
|
+
const result = await this.runScript(userId, requestId);
|
|
40
|
+
return Boolean(result);
|
|
41
|
+
}
|
|
42
|
+
async runScript(userId, requestId) {
|
|
43
|
+
const result = await this.client.eval(this.script, {
|
|
44
|
+
keys: [`sliding_window:${userId}`],
|
|
45
|
+
arguments: [
|
|
46
|
+
String(Date.now()),
|
|
47
|
+
String(this.maxRequests),
|
|
48
|
+
String(this.windowSize),
|
|
49
|
+
requestId
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export { SlidingWindow, };
|
|
56
|
+
//# sourceMappingURL=slidingWindow.algo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slidingWindow.algo.js","sourceRoot":"","sources":["../../../src/rate_limiter/algorithms/slidingWindow.algo.ts"],"names":[],"mappings":"AAGA,MAAM,aAAa;IACP,UAAU,CAAS;IACnB,WAAW,CAAS;IACpB,MAAM,CAAkB;IAEf,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;KA0BzB,CAAA;IACD,YAAY,UAAkB,EAAE,WAAmB,EAAE,MAAuB;QACxE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAGD,KAAK,CAAC,YAAY,CAAC,MAAc;QAC7B,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACvD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAGD,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,SAAiB;QAC7C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAC/C,IAAI,EAAE,CAAC,kBAAkB,MAAM,EAAE,CAAC;YAClC,SAAS,EAAE;gBACP,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;gBACxB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;gBACvB,SAAS;aACZ;SACJ,CAAC,CAAC;QACH,OAAO,MAAgB,CAAC;IAC5B,CAAC;CAEJ;AAED,OAAM,EACF,aAAa,GAChB,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import type { RateLimiter } from "../rateLimiter.interface.js";
|
|
3
|
+
declare class TokenBucket implements RateLimiter {
|
|
4
|
+
private capacity;
|
|
5
|
+
private refillRate;
|
|
6
|
+
private client;
|
|
7
|
+
private readonly script;
|
|
8
|
+
constructor(capacity: number, refillRate: number, client: RedisClientType);
|
|
9
|
+
allowRequest(userId: string): Promise<boolean>;
|
|
10
|
+
runScript(userId: string): Promise<string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | (string | number | bigint | boolean | /*elided*/ any | import("redis").BlobError | import("redis").SimpleError | {
|
|
11
|
+
[x: string]: string | number | bigint | boolean | /*elided*/ any | import("redis").BlobError | import("redis").SimpleError | /*elided*/ any | null;
|
|
12
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
13
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
14
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
15
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
16
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
17
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
18
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
19
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
20
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
21
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
22
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
23
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
24
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
25
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
26
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
27
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
28
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
29
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
30
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
31
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
32
|
+
} | null)[] | import("redis").BlobError | import("redis").SimpleError | {
|
|
33
|
+
[x: string]: string | number | bigint | boolean | any | import("redis").BlobError | import("redis").SimpleError | any | null;
|
|
34
|
+
} | null>;
|
|
35
|
+
}
|
|
36
|
+
export { TokenBucket, };
|
|
37
|
+
//# sourceMappingURL=tokenBucket.algo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenBucket.algo.d.ts","sourceRoot":"","sources":["../../../src/rate_limiter/algorithms/tokenBucket.algo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,cAAM,WAAY,YAAW,WAAW;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAyDrB;IAEF,YAAY,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAIxE;IAEK,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnD;IAEK,SAAS,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;cAU7B;CAGJ;AAGD,OAAO,EACH,WAAW,GACd,CAAA"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
class TokenBucket {
|
|
2
|
+
capacity;
|
|
3
|
+
refillRate;
|
|
4
|
+
client;
|
|
5
|
+
script = `
|
|
6
|
+
local now = tonumber(ARGV[3])
|
|
7
|
+
local capacity = tonumber(ARGV[1])
|
|
8
|
+
local refillRate = tonumber(ARGV[2])
|
|
9
|
+
|
|
10
|
+
local tokens = tonumber(
|
|
11
|
+
redis.call("HGET", KEYS[1], "tokens")
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
local lastRefillTime
|
|
15
|
+
|
|
16
|
+
if tokens == nil then
|
|
17
|
+
|
|
18
|
+
tokens = capacity
|
|
19
|
+
lastRefillTime = now
|
|
20
|
+
|
|
21
|
+
redis.call("HSET", KEYS[1],
|
|
22
|
+
"tokens", tokens,
|
|
23
|
+
"lastRefillTime", lastRefillTime
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
else
|
|
27
|
+
|
|
28
|
+
lastRefillTime = tonumber(
|
|
29
|
+
redis.call("HGET", KEYS[1], "lastRefillTime")
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
local elapsedTime = (now - lastRefillTime) / 1000
|
|
35
|
+
|
|
36
|
+
local tokensToAdd = elapsedTime * refillRate
|
|
37
|
+
|
|
38
|
+
local newTokens = math.min(
|
|
39
|
+
capacity,
|
|
40
|
+
tokens + tokensToAdd
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if newTokens >= 1 then
|
|
44
|
+
|
|
45
|
+
newTokens = newTokens - 1
|
|
46
|
+
|
|
47
|
+
redis.call("HSET", KEYS[1],
|
|
48
|
+
"tokens", newTokens,
|
|
49
|
+
"lastRefillTime", now
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return 1
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
redis.call("HSET", KEYS[1],
|
|
57
|
+
"tokens", newTokens,
|
|
58
|
+
"lastRefillTime", now
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return 0
|
|
62
|
+
`;
|
|
63
|
+
constructor(capacity, refillRate, client) {
|
|
64
|
+
this.capacity = capacity;
|
|
65
|
+
this.refillRate = refillRate;
|
|
66
|
+
this.client = client;
|
|
67
|
+
}
|
|
68
|
+
async allowRequest(userId) {
|
|
69
|
+
const result = await this.runScript(userId);
|
|
70
|
+
if (result === 1) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
async runScript(userId) {
|
|
76
|
+
const result = await this.client.eval(this.script, {
|
|
77
|
+
keys: [`token_bucket:${userId}`],
|
|
78
|
+
arguments: [
|
|
79
|
+
String(this.capacity),
|
|
80
|
+
String(this.refillRate),
|
|
81
|
+
String(Date.now()),
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export { TokenBucket, };
|
|
88
|
+
//# sourceMappingURL=tokenBucket.algo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenBucket.algo.js","sourceRoot":"","sources":["../../../src/rate_limiter/algorithms/tokenBucket.algo.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW;IACL,QAAQ,CAAS;IACjB,UAAU,CAAS;IACnB,MAAM,CAAkB;IACf,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAyDzB,CAAC;IAEF,YAAY,QAAgB,EAAE,UAAkB,EAAE,MAAuB;QACrE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAE5C,IAAG,MAAM,KAAK,CAAC,EAAC,CAAC;YACb,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAc;QAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAC/C,IAAI,EAAE,CAAC,gBAAgB,MAAM,EAAE,CAAC;YAChC,SAAS,EAAE;gBACP,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;aACrB;SACJ,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAClB,CAAC;CAGJ;AAGD,OAAO,EACH,WAAW,GACd,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import type { RateLimiter } from "./rateLimiter.interface.js";
|
|
3
|
+
type RateLimiterOptions = {
|
|
4
|
+
algorithm: "token-bucket";
|
|
5
|
+
capacity: number;
|
|
6
|
+
refillRate: number;
|
|
7
|
+
client: RedisClientType;
|
|
8
|
+
} | {
|
|
9
|
+
algorithm: "leaky-bucket";
|
|
10
|
+
capacity: number;
|
|
11
|
+
leakRate: number;
|
|
12
|
+
client: RedisClientType;
|
|
13
|
+
} | {
|
|
14
|
+
algorithm: "sliding-window";
|
|
15
|
+
windowSize: number;
|
|
16
|
+
maxRequests: number;
|
|
17
|
+
client: RedisClientType;
|
|
18
|
+
};
|
|
19
|
+
declare function createRateLimiter(options: RateLimiterOptions): RateLimiter;
|
|
20
|
+
export { createRateLimiter };
|
|
21
|
+
export type { RateLimiterOptions };
|
|
22
|
+
//# sourceMappingURL=rateLimiter.factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.factory.d.ts","sourceRoot":"","sources":["../../src/rate_limiter/rateLimiter.factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAK9D,KAAK,kBAAkB,GACjB;IACE,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;CAC3B,GACC;IACE,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;CAC3B,GACC;IACE,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;CAC3B,CAAC;AAEN,iBAAS,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAuBnE;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { TokenBucket } from "./algorithms/tokenBucket.algo.js";
|
|
2
|
+
import { LeakyBucket } from "./algorithms/leakyBucket.algo.js";
|
|
3
|
+
import { SlidingWindow } from "./algorithms/slidingWindow.algo.js";
|
|
4
|
+
function createRateLimiter(options) {
|
|
5
|
+
switch (options.algorithm) {
|
|
6
|
+
case "token-bucket":
|
|
7
|
+
return new TokenBucket(options.capacity, options.refillRate, options.client);
|
|
8
|
+
case "leaky-bucket":
|
|
9
|
+
return new LeakyBucket(options.capacity, options.leakRate, options.client);
|
|
10
|
+
case "sliding-window":
|
|
11
|
+
return new SlidingWindow(options.windowSize, options.maxRequests, options.client);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export { createRateLimiter };
|
|
15
|
+
//# sourceMappingURL=rateLimiter.factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.factory.js","sourceRoot":"","sources":["../../src/rate_limiter/rateLimiter.factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAsBnE,SAAS,iBAAiB,CAAC,OAA2B;IAClD,QAAQ,OAAO,CAAC,SAAS,EAAE,CAAC;QACxB,KAAK,cAAc;YACf,OAAO,IAAI,WAAW,CAClB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,MAAM,CACjB,CAAC;QAEN,KAAK,cAAc;YACf,OAAO,IAAI,WAAW,CAClB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,MAAM,CACjB,CAAC;QAEN,KAAK,gBAAgB;YACjB,OAAO,IAAI,aAAa,CACpB,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,WAAW,EACnB,OAAO,CAAC,MAAM,CACjB,CAAC;IACV,CAAC;AACL,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.interface.d.ts","sourceRoot":"","sources":["../../src/rate_limiter/rateLimiter.interface.ts"],"names":[],"mappings":"AAAA,UAAU,WAAW;IACjB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAClD;AAED,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.interface.js","sourceRoot":"","sources":["../../src/rate_limiter/rateLimiter.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic_cache.config.d.ts","sourceRoot":"","sources":["../../../src/semantic_cache/config/semantic_cache.config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic_cache.config.js","sourceRoot":"","sources":["../../../src/semantic_cache/config/semantic_cache.config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import type { LLMProvider } from "../../interfaces/llm.interface.js";
|
|
3
|
+
import type { EmbeddingProvider } from "../../interfaces/embedding.interface.js";
|
|
4
|
+
type SemanticCacheOptions = {
|
|
5
|
+
client: RedisClientType;
|
|
6
|
+
embeddingProvider: EmbeddingProvider;
|
|
7
|
+
llmProvider: LLMProvider;
|
|
8
|
+
threshold?: number;
|
|
9
|
+
};
|
|
10
|
+
declare class SemanticCache {
|
|
11
|
+
private client;
|
|
12
|
+
private embeddingProvider;
|
|
13
|
+
private llmProvider;
|
|
14
|
+
private threshold;
|
|
15
|
+
constructor(options: SemanticCacheOptions);
|
|
16
|
+
private createCacheService;
|
|
17
|
+
private getCacheService;
|
|
18
|
+
private searchCacheService;
|
|
19
|
+
private isCacheHit;
|
|
20
|
+
getCachedOrGenerate(query: string): Promise<string>;
|
|
21
|
+
}
|
|
22
|
+
export { SemanticCache };
|
|
23
|
+
//# sourceMappingURL=semantic_cache.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic_cache.service.d.ts","sourceRoot":"","sources":["../../../src/semantic_cache/services/semantic_cache.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAG7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAIjF,KAAK,oBAAoB,GAAG;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAkBF,cAAM,aAAa;IACf,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,OAAO,EAAE,oBAAoB,EAKxC;YAEa,kBAAkB;YAgBlB,eAAe;YAKf,kBAAkB;IAgDhC,OAAO,CAAC,UAAU;IAIL,mBAAmB,CAAC,KAAK,EAAE,MAAM,mBAY7C;CACJ;AAED,OAAO,EACH,aAAa,EAChB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { embeddingToBuffer } from "../utils/vector.utils.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { CACHE_SIMILARITY_THRESHOLD } from "../config/semantic_cache.config.js";
|
|
4
|
+
class SemanticCache {
|
|
5
|
+
client;
|
|
6
|
+
embeddingProvider;
|
|
7
|
+
llmProvider;
|
|
8
|
+
threshold;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.client = options.client;
|
|
11
|
+
this.embeddingProvider = options.embeddingProvider;
|
|
12
|
+
this.llmProvider = options.llmProvider;
|
|
13
|
+
this.threshold = options.threshold ?? CACHE_SIMILARITY_THRESHOLD;
|
|
14
|
+
}
|
|
15
|
+
async createCacheService(query, response) {
|
|
16
|
+
const cacheId = crypto.randomUUID();
|
|
17
|
+
const embedding = await this.embeddingProvider.generateEmbedding(query);
|
|
18
|
+
const embeddingBuffer = embeddingToBuffer(embedding);
|
|
19
|
+
await this.client.hSet(`semantic_cache:${cacheId}`, {
|
|
20
|
+
query,
|
|
21
|
+
response,
|
|
22
|
+
embedding: embeddingBuffer,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async getCacheService(cache_id, client) {
|
|
26
|
+
const cacheEntry = await client.hGetAll(`semantic_cache:${cache_id}`);
|
|
27
|
+
return cacheEntry;
|
|
28
|
+
}
|
|
29
|
+
async searchCacheService(query) {
|
|
30
|
+
const embedding = await this.embeddingProvider.generateEmbedding(query);
|
|
31
|
+
const embeddingBuffer = embeddingToBuffer(embedding);
|
|
32
|
+
const result = await this.client.sendCommand([
|
|
33
|
+
"FT.SEARCH",
|
|
34
|
+
"semantic_cache_idx",
|
|
35
|
+
"*=>[KNN 1 @embedding $query_vector AS vector_score]",
|
|
36
|
+
"PARAMS",
|
|
37
|
+
"2",
|
|
38
|
+
"query_vector",
|
|
39
|
+
embeddingBuffer,
|
|
40
|
+
"SORTBY",
|
|
41
|
+
"vector_score",
|
|
42
|
+
"DIALECT",
|
|
43
|
+
"2",
|
|
44
|
+
"RETURN",
|
|
45
|
+
"3",
|
|
46
|
+
"query",
|
|
47
|
+
"response",
|
|
48
|
+
"vector_score",
|
|
49
|
+
]);
|
|
50
|
+
if (result.total_results === 0) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const cacheEntry = result.results[0];
|
|
54
|
+
if (!cacheEntry) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const distance = Number(cacheEntry.extra_attributes.vector_score);
|
|
58
|
+
const similarityScore = Math.max(0, Math.min(1, 1 - distance));
|
|
59
|
+
return {
|
|
60
|
+
id: cacheEntry.id,
|
|
61
|
+
query: cacheEntry.extra_attributes.query,
|
|
62
|
+
response: cacheEntry.extra_attributes.response,
|
|
63
|
+
similarityScore: similarityScore,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
isCacheHit(similarityScore) {
|
|
67
|
+
return similarityScore >= this.threshold;
|
|
68
|
+
}
|
|
69
|
+
async getCachedOrGenerate(query) {
|
|
70
|
+
const cacheEntry = await this.searchCacheService(query);
|
|
71
|
+
if (cacheEntry && this.isCacheHit(cacheEntry.similarityScore)) {
|
|
72
|
+
console.log("cachehit");
|
|
73
|
+
return cacheEntry.response;
|
|
74
|
+
}
|
|
75
|
+
console.log("cachemiss");
|
|
76
|
+
const response = await this.llmProvider.askLLM(query);
|
|
77
|
+
await this.createCacheService(query, response);
|
|
78
|
+
return response;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export { SemanticCache };
|
|
82
|
+
//# sourceMappingURL=semantic_cache.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic_cache.service.js","sourceRoot":"","sources":["../../../src/semantic_cache/services/semantic_cache.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AA6BhF,MAAM,aAAa;IACP,MAAM,CAAkB;IACxB,iBAAiB,CAAoB;IACrC,WAAW,CAAc;IACzB,SAAS,CAAS;IAE1B,YAAY,OAA6B;QACrC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACnD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,0BAA0B,CAAC;IACrE,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC5B,KAAa,EACb,QAAgB;QAEhB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAEpC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACxE,MAAM,eAAe,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAErD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,OAAO,EAAE,EAAE;YAChD,KAAK;YACL,QAAQ;YACR,SAAS,EAAE,eAAe;SAC7B,CAAC,CAAC;IACP,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,QAAgB,EAAE,MAAuB;QACnE,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;QACtE,OAAO,UAAU,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,KAAa;QAC1C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACxE,MAAM,eAAe,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAErD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YACzC,WAAW;YACX,oBAAoB;YACpB,qDAAqD;YACrD,QAAQ;YACR,GAAG;YACH,cAAc;YACd,eAAe;YACf,QAAQ;YACR,cAAc;YACd,SAAS;YACT,GAAG;YACH,QAAQ;YACR,GAAG;YACH,OAAO;YACP,UAAU;YACV,cAAc;SACjB,CAA4B,CAAC;QAE9B,IAAI,MAAM,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAErC,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAElE,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAC5B,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,CAC5B,CAAC;QAEF,OAAO;YACH,EAAE,EAAE,UAAU,CAAC,EAAE;YACjB,KAAK,EAAE,UAAU,CAAC,gBAAgB,CAAC,KAAK;YACxC,QAAQ,EAAE,UAAU,CAAC,gBAAgB,CAAC,QAAQ;YAC9C,eAAe,EAAE,eAAe;SACnC,CAAC;IACN,CAAC;IAEO,UAAU,CAAC,eAAuB;QACtC,OAAO,eAAe,IAAI,IAAI,CAAC,SAAS,CAAC;IAC7C,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAAC,KAAa;QAC1C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAExD,IAAI,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YAC5D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACxB,OAAO,UAAU,CAAC,QAAQ,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC/C,OAAO,QAAQ,CAAC;IACpB,CAAC;CACJ;AAED,OAAO,EACH,aAAa,EAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.utils.d.ts","sourceRoot":"","sources":["../../../src/semantic_cache/utils/similarity.utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAC5B,CAAC,EAAE,MAAM,EAAE,EACX,CAAC,EAAE,MAAM,EAAE,GACZ,MAAM,CAoBR"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function cosineSimilarity(a, b) {
|
|
2
|
+
if (a.length !== b.length) {
|
|
3
|
+
throw new Error("Vectors must have the same dimensions");
|
|
4
|
+
}
|
|
5
|
+
let dotProduct = 0;
|
|
6
|
+
let magnitudeA = 0;
|
|
7
|
+
let magnitudeB = 0;
|
|
8
|
+
for (let i = 0; i < a.length; i++) {
|
|
9
|
+
dotProduct += a[i] * b[i];
|
|
10
|
+
magnitudeA += a[i] * a[i];
|
|
11
|
+
magnitudeB += b[i] * b[i];
|
|
12
|
+
}
|
|
13
|
+
if (magnitudeA === 0 || magnitudeB === 0) {
|
|
14
|
+
throw new Error("Cannot calculate similarity for zero vectors");
|
|
15
|
+
}
|
|
16
|
+
return dotProduct / (Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=similarity.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.utils.js","sourceRoot":"","sources":["../../../src/semantic_cache/utils/similarity.utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAC5B,CAAW,EACX,CAAW;IAEX,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAC5B,UAAU,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAC5B,UAAU,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IAChC,CAAC;IAED,IAAI,UAAU,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector.utils.d.ts","sourceRoot":"","sources":["../../../src/semantic_cache/utils/vector.utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAI7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector.utils.js","sourceRoot":"","sources":["../../../src/semantic_cache/utils/vector.utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,SAAmB;IACjD,MAAM,gBAAgB,GAAG,IAAI,YAAY,CAAC,SAAS,CAAC,CAAC;IAErD,OAAO,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rupayan-das/request-guard",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Redis-based rate limiting and semantic caching for Node.js applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "vitest",
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"clean": "rimraf dist",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"rate-limiter",
|
|
25
|
+
"rate-limit",
|
|
26
|
+
"redis",
|
|
27
|
+
"semantic-cache",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "Rupayan Das",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^26.4.0",
|
|
34
|
+
"rimraf": "^6.1.3",
|
|
35
|
+
"typescript": "^7.0.2",
|
|
36
|
+
"vitest": "^4.1.11"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"redis": "^6.2.1"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/Rupayan-san/request-guard.git"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/Rupayan-san/request-guard/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/Rupayan-san/request-guard#readme"
|
|
52
|
+
}
|