ttc-rate-limit 0.1.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/README.md +193 -0
- package/dist/cluster.d.ts +19 -0
- package/dist/cluster.d.ts.map +1 -0
- package/dist/cluster.js +54 -0
- package/dist/cluster.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/ratelimit.d.ts +43 -0
- package/dist/ratelimit.d.ts.map +1 -0
- package/dist/ratelimit.js +161 -0
- package/dist/ratelimit.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +24 -0
- package/dist/test.js.map +1 -0
- package/dist/time.d.ts +11 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +32 -0
- package/dist/time.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# ttc_rate_limit
|
|
2
|
+
|
|
3
|
+
A rate limiting library with worker pool support for parallel timeout management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install ttc_rate_limit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Rate Limiting**: Configurable rate limits per second, minute, hour, or day
|
|
14
|
+
- **Worker Pool Support**: Parallel timeout management using worker threads
|
|
15
|
+
- **Multiple Strategies**: Spread and burst modes for different use cases
|
|
16
|
+
- **Cluster Support**: Distribute rate limiting across multiple limiters
|
|
17
|
+
- **Event-Driven**: Listen for completion and error events
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Single Rate Limiter
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { RateLimiter } from 'ttc_rate_limit';
|
|
25
|
+
|
|
26
|
+
// Create a rate limiter for API calls
|
|
27
|
+
const apiLimiter = new RateLimiter({
|
|
28
|
+
id: 'api-limiter',
|
|
29
|
+
request: 100, // 100 requests
|
|
30
|
+
per: 'minute', // per minute
|
|
31
|
+
mode: 'spread', // evenly spread requests
|
|
32
|
+
cb: async (data) => {
|
|
33
|
+
// Your async function to rate limit
|
|
34
|
+
const response = await fetch('https://api.example.com/data', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
body: JSON.stringify(data)
|
|
37
|
+
});
|
|
38
|
+
return await response.json();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Listen for completed requests
|
|
43
|
+
apiLimiter.on('completed', (data) => {
|
|
44
|
+
console.log('Request completed:', data.response);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Listen for errors
|
|
48
|
+
apiLimiter.on('error', (data) => {
|
|
49
|
+
console.error('Request failed:', data.response);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Make requests through the limiter
|
|
53
|
+
for (let i = 0; i < 150; i++) {
|
|
54
|
+
const result = await apiLimiter.invoke({
|
|
55
|
+
userId: 'user123',
|
|
56
|
+
action: `action-${i}`
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Result can be: { status: 'success' }, { status: 'queued' }, or { status: 'error', message: string }
|
|
60
|
+
console.log(`Request ${i}: ${result.status}`);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Burst Mode Example
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { RateLimiter } from 'ttc_rate_limit';
|
|
68
|
+
|
|
69
|
+
// Create a burst mode limiter for batch processing
|
|
70
|
+
const burstLimiter = new RateLimiter({
|
|
71
|
+
id: 'batch-processor',
|
|
72
|
+
request: 50, // 50 requests
|
|
73
|
+
per: 'second', // per second
|
|
74
|
+
mode: 'burst', // allow bursts up to limit
|
|
75
|
+
cb: async (batch) => {
|
|
76
|
+
// Process batch of data
|
|
77
|
+
return await processBatch(batch);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Time Cluster (Worker Pool)
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { TimeCluster } from 'ttc_rate_limit';
|
|
86
|
+
|
|
87
|
+
// Create a time cluster with intervals
|
|
88
|
+
const timeCluster = new TimeCluster(20); // 20 intervals
|
|
89
|
+
|
|
90
|
+
// Wait for async operation with timeout
|
|
91
|
+
await timeCluster.waitFor(async () => {
|
|
92
|
+
console.log('Executed after 2.5 seconds');
|
|
93
|
+
}, 2.5); // 2.5 seconds timeout
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Rate Limiter Cluster
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { Cluster } from 'ttc_rate_limit';
|
|
100
|
+
|
|
101
|
+
type Task = {
|
|
102
|
+
modelId: string,
|
|
103
|
+
chatId: string,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Create a cluster with round-robin distribution
|
|
107
|
+
const cluster = new Cluster<Task>('roundrobin');
|
|
108
|
+
|
|
109
|
+
// Add rate limiters to the cluster
|
|
110
|
+
cluster.addOption({
|
|
111
|
+
id: 'model-a',
|
|
112
|
+
request: 100,
|
|
113
|
+
per: 'minute',
|
|
114
|
+
mode: 'spread',
|
|
115
|
+
cb: async (input) => {
|
|
116
|
+
return `Processed by Model A: ${input.modelId} - ${input.chatId}`;
|
|
117
|
+
}
|
|
118
|
+
}, true); // true = add immediately
|
|
119
|
+
|
|
120
|
+
cluster.addOption({
|
|
121
|
+
id: 'model-b',
|
|
122
|
+
request: 200,
|
|
123
|
+
per: 'minute',
|
|
124
|
+
mode: 'burst',
|
|
125
|
+
cb: async (input) => {
|
|
126
|
+
return `Processed by Model B: ${input.modelId} - ${input.chatId}`;
|
|
127
|
+
}
|
|
128
|
+
}, true);
|
|
129
|
+
|
|
130
|
+
// Listen to cluster events
|
|
131
|
+
cluster.on('completed', (data) => {
|
|
132
|
+
console.log('Cluster completed:', data.response);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
cluster.on('error', (data) => {
|
|
136
|
+
console.error('Cluster error:', data.response);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Distribute requests across the cluster
|
|
140
|
+
for (let i = 0; i < 500; i++) {
|
|
141
|
+
await cluster.invoke({
|
|
142
|
+
modelId: 'gpt-4',
|
|
143
|
+
chatId: `chat-${i}`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## API
|
|
149
|
+
|
|
150
|
+
### RateLimiter
|
|
151
|
+
|
|
152
|
+
Main rate limiting class with configurable limits and modes.
|
|
153
|
+
|
|
154
|
+
**Constructor:**
|
|
155
|
+
```typescript
|
|
156
|
+
new RateLimiter<T>(config: RateLimiterConfig<T>)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Methods:**
|
|
160
|
+
- `invoke(input: T): Promise<{ status: 'success' | 'error' | 'queued', message?: string }>` - Process a request
|
|
161
|
+
- `on(event: 'completed' | 'error', listener): void` - Listen for events
|
|
162
|
+
|
|
163
|
+
### TimeCluster
|
|
164
|
+
|
|
165
|
+
Worker pool for managing parallel timeouts with callback registry.
|
|
166
|
+
|
|
167
|
+
**Constructor:**
|
|
168
|
+
```typescript
|
|
169
|
+
new TimeCluster(workerCount: number)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Methods:**
|
|
173
|
+
- `setTimeout(callback: () => void, delayMs: number): string` - Schedule timeout
|
|
174
|
+
- `clearTimeout(timeoutId: string): void` - Cancel timeout
|
|
175
|
+
- `waitFor(callback: () => Promise<void>, delaySeconds: number): Promise<void>` - Wait with async callback
|
|
176
|
+
|
|
177
|
+
### Cluster
|
|
178
|
+
|
|
179
|
+
Cluster of rate limiters for distributed rate limiting.
|
|
180
|
+
|
|
181
|
+
**Constructor:**
|
|
182
|
+
```typescript
|
|
183
|
+
new Cluster<T>(strategy: 'roundrobin')
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Methods:**
|
|
187
|
+
- `addOption(config: RateLimiterConfig<T>, addImmediately: boolean): void` - Add rate limiter option
|
|
188
|
+
- `invoke(input: T): Promise<any>` - Distribute request to cluster
|
|
189
|
+
- `on(event: 'completed' | 'error', listener): void` - Listen for cluster events
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RateLimiter, RateLimiterConfig } from "./ratelimit";
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export declare class Cluster<T> {
|
|
4
|
+
config: {
|
|
5
|
+
type: 'random' | 'roundrobin' | 'prefered';
|
|
6
|
+
perferedLimiter?: string;
|
|
7
|
+
};
|
|
8
|
+
emitter: EventEmitter;
|
|
9
|
+
limiters: RateLimiter<T>[];
|
|
10
|
+
constructor(type: 'random' | 'roundrobin' | 'prefered');
|
|
11
|
+
on: (event: "completed" | "error", listener: (data: {
|
|
12
|
+
request: T;
|
|
13
|
+
response: any;
|
|
14
|
+
}) => void) => void;
|
|
15
|
+
addOption: (config: RateLimiterConfig<T>, specific?: boolean) => this;
|
|
16
|
+
invoke: (item: T) => Promise<void>;
|
|
17
|
+
randomLimiter: () => RateLimiter<T>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=cluster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../src/cluster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,qBAAa,OAAO,CAAC,CAAC;IAElB,MAAM,EAAE;QACJ,IAAI,EAAE,QAAQ,GAAG,YAAY,GAAG,UAAU,CAAC;QAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAsB;IAEvB,OAAO,EAAE,YAAY,CAAsB;IAE3C,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAM;gBAEpB,IAAI,EAAE,QAAQ,GAAG,YAAY,GAAG,UAAU;IAMtD,EAAE,GAAI,OAAO,WAAW,GAAG,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE;QAAC,OAAO,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,GAAG,CAAA;KAAC,KAAK,IAAI,UAExF;IAED,SAAS,GAAI,QAAQ,iBAAiB,CAAC,CAAC,CAAC,EAAE,WAAU,OAAe,UAWnE;IAED,MAAM,GAAU,MAAM,CAAC,mBAmBtB;IAED,aAAa,QAAO,WAAW,CAAC,CAAC,CAAC,CAGjC;CACJ"}
|
package/dist/cluster.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { RateLimiter } from "./ratelimit";
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export class Cluster {
|
|
4
|
+
config = { type: 'random' };
|
|
5
|
+
emitter = new EventEmitter();
|
|
6
|
+
limiters = [];
|
|
7
|
+
constructor(type) {
|
|
8
|
+
this.config = {
|
|
9
|
+
type
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
on = (event, listener) => {
|
|
13
|
+
this.emitter.on(event, listener);
|
|
14
|
+
};
|
|
15
|
+
addOption = (config, specific = false) => {
|
|
16
|
+
const rl = new RateLimiter(config);
|
|
17
|
+
this.limiters.push(rl);
|
|
18
|
+
this.config.perferedLimiter = specific ? rl.config.id : this.config.perferedLimiter;
|
|
19
|
+
rl.on('error', async (data) => {
|
|
20
|
+
this.emitter.emit('error', data);
|
|
21
|
+
});
|
|
22
|
+
rl.on('completed', async (data) => {
|
|
23
|
+
this.emitter.emit('completed', data);
|
|
24
|
+
});
|
|
25
|
+
return this;
|
|
26
|
+
};
|
|
27
|
+
invoke = async (item) => {
|
|
28
|
+
try {
|
|
29
|
+
let limiter;
|
|
30
|
+
if (this.config.type === 'random') {
|
|
31
|
+
limiter = this.randomLimiter();
|
|
32
|
+
}
|
|
33
|
+
else if (this.config.type === 'roundrobin') {
|
|
34
|
+
limiter = this.limiters.shift();
|
|
35
|
+
this.limiters.push(limiter);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
limiter = this.limiters.find(l => l.config.id === this.config.perferedLimiter);
|
|
39
|
+
if (!limiter) {
|
|
40
|
+
limiter = this.randomLimiter();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await limiter.invoke(item);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
randomLimiter = () => {
|
|
50
|
+
const idx = Math.floor(Math.random() * this.limiters.length);
|
|
51
|
+
return this.limiters[idx];
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=cluster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster.js","sourceRoot":"","sources":["../src/cluster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAqB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,OAAO,OAAO;IAEhB,MAAM,GAGF,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAEvB,OAAO,GAAiB,IAAI,YAAY,EAAE,CAAC;IAE3C,QAAQ,GAAqB,EAAE,CAAC;IAEhC,YAAY,IAA0C;QAClD,IAAI,CAAC,MAAM,GAAG;YACV,IAAI;SACP,CAAA;IACL,CAAC;IAED,EAAE,GAAG,CAAC,KAA4B,EAAE,QAAqD,EAAE,EAAE;QACzF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC,CAAA;IAED,SAAS,GAAG,CAAC,MAA4B,EAAE,WAAoB,KAAK,EAAE,EAAE;QACpE,MAAM,EAAE,GAAG,IAAI,WAAW,CAAI,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QACpF,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC,CAAC,CAAA;QACF,EAAE,CAAC,EAAE,CAAC,WAAW,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IAChB,CAAC,CAAA;IAED,MAAM,GAAG,KAAK,EAAE,IAAO,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,IAAI,OAAuB,CAAC;YAC5B,IAAG,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC/B,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YACnC,CAAC;iBAAM,IAAG,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1C,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAG,CAAC;gBACjC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACJ,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,CAAQ,CAAC;gBACtF,IAAG,CAAC,OAAO,EAAE,CAAC;oBACX,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBAClC,CAAC;YACL,CAAC;YAED,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACZ,MAAM,KAAK,CAAC;QACjB,CAAC;IACJ,CAAC,CAAA;IAED,aAAa,GAAG,GAAmB,EAAE;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC,CAAA;CACJ"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA0C,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { TimeCluster } from "./time";
|
|
3
|
+
export declare const time: TimeCluster;
|
|
4
|
+
export interface RateLimiterConfig<T> {
|
|
5
|
+
id: string;
|
|
6
|
+
request: number;
|
|
7
|
+
per: 'second' | 'minute' | 'hour' | 'day';
|
|
8
|
+
interval?: number;
|
|
9
|
+
cb: (input: T) => Promise<any>;
|
|
10
|
+
_count?: number;
|
|
11
|
+
rps?: number;
|
|
12
|
+
_Tmarker?: number;
|
|
13
|
+
mode: 'spread' | 'burst';
|
|
14
|
+
}
|
|
15
|
+
export type queueData<T> = {
|
|
16
|
+
data: T;
|
|
17
|
+
cb: (response: any) => Promise<any>;
|
|
18
|
+
};
|
|
19
|
+
export declare class RateLimiter<T> {
|
|
20
|
+
config: RateLimiterConfig<T>;
|
|
21
|
+
queue: T[];
|
|
22
|
+
eventManager: EventEmitter;
|
|
23
|
+
constructor(config: RateLimiterConfig<T>);
|
|
24
|
+
_resolve(): void;
|
|
25
|
+
on: (event: "completed" | "error", listener: (data: {
|
|
26
|
+
request: T;
|
|
27
|
+
response: any;
|
|
28
|
+
}) => void) => void;
|
|
29
|
+
processQueue(): Promise<void>;
|
|
30
|
+
spreadAlgorithm(input: T): Promise<{
|
|
31
|
+
status: 'success' | 'error' | 'queued';
|
|
32
|
+
message?: string | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
burstAlgorithm(input: T): Promise<{
|
|
35
|
+
status: 'success' | 'error' | 'queued';
|
|
36
|
+
message?: string | undefined;
|
|
37
|
+
}>;
|
|
38
|
+
invoke(input: T): Promise<{
|
|
39
|
+
status: 'success' | 'error' | 'queued';
|
|
40
|
+
message?: string | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ratelimit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.d.ts","sourceRoot":"","sources":["../src/ratelimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAErC,eAAO,MAAM,IAAI,aAAsB,CAAC;AAExC,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACvB,IAAI,EAAE,CAAC,CAAC;IACR,EAAE,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACvC,CAAA;AAED,qBAAa,WAAW,CAAC,CAAC;IAEtB,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,EAAE,CAAM;IAChB,YAAY,EAAE,YAAY,CAAC;gBACf,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAMxC,QAAQ;IAcR,EAAE,GAAI,OAAO,WAAW,GAAG,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE;QACjD,OAAO,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,GAAG,CAAA;KAC5B,KAAK,IAAI,UAET;IAEK,YAAY;IA6BZ,eAAe,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QACrC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;QACvC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC;IAmCI,cAAc,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QACpC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;QACvC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC;IA4DI,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAC5B,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;QACvC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC;CAiBL"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { TimeCluster } from "./time";
|
|
3
|
+
export const time = new TimeCluster(20);
|
|
4
|
+
export class RateLimiter {
|
|
5
|
+
config;
|
|
6
|
+
queue = [];
|
|
7
|
+
eventManager;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this._resolve();
|
|
11
|
+
this.eventManager = new EventEmitter();
|
|
12
|
+
}
|
|
13
|
+
_resolve() {
|
|
14
|
+
const intervals = {
|
|
15
|
+
'second': 1,
|
|
16
|
+
'minute': 60,
|
|
17
|
+
'hour': 3600,
|
|
18
|
+
'day': 86400
|
|
19
|
+
};
|
|
20
|
+
const interval = intervals[this.config.per] ?? 1;
|
|
21
|
+
this.config.rps = Math.ceil(this.config.request / interval);
|
|
22
|
+
this.config.interval = interval / this.config.request;
|
|
23
|
+
this.config._count = 0;
|
|
24
|
+
console.log(this.config);
|
|
25
|
+
}
|
|
26
|
+
on = (event, listener) => {
|
|
27
|
+
this.eventManager.on(event, listener);
|
|
28
|
+
};
|
|
29
|
+
async processQueue() {
|
|
30
|
+
if (this.config.mode === 'spread') {
|
|
31
|
+
while (this.queue.length > 0 && this.config._count < this.config.rps) {
|
|
32
|
+
const item = this.queue.shift();
|
|
33
|
+
await this.invoke(item);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
while (this.queue.length > 0) {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const windowMs = this.config.interval * 1000;
|
|
40
|
+
// Reset if window expired
|
|
41
|
+
if (this.config._Tmarker && now - this.config._Tmarker >= windowMs) {
|
|
42
|
+
this.config._count = 0;
|
|
43
|
+
this.config._Tmarker = now;
|
|
44
|
+
}
|
|
45
|
+
// If we have capacity in current window
|
|
46
|
+
if (this.config._count < this.config.request) {
|
|
47
|
+
const item = this.queue.shift();
|
|
48
|
+
await this.invoke(item);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Wait for window reset
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async spreadAlgorithm(input) {
|
|
58
|
+
if (this.config._count < this.config.rps) {
|
|
59
|
+
this.config._count++;
|
|
60
|
+
if (this.config._count >= this.config.rps) {
|
|
61
|
+
await time.waitFor(async () => {
|
|
62
|
+
this.config._count = 0;
|
|
63
|
+
await this.processQueue();
|
|
64
|
+
}, this.config.interval);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const response = await this.config.cb(input);
|
|
68
|
+
this.eventManager.emit('completed', {
|
|
69
|
+
request: input,
|
|
70
|
+
response
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
status: 'success',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
status: 'error',
|
|
79
|
+
message: error.message
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.queue.push(input);
|
|
85
|
+
return {
|
|
86
|
+
status: 'queued',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async burstAlgorithm(input) {
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const windowMs = this.config.interval * 1000;
|
|
93
|
+
// Reset count if window has passed
|
|
94
|
+
if (this.config._Tmarker && now - this.config._Tmarker >= windowMs) {
|
|
95
|
+
this.config._count = 0;
|
|
96
|
+
}
|
|
97
|
+
// Initialize marker if starting fresh
|
|
98
|
+
if (this.config._count === 0) {
|
|
99
|
+
this.config._Tmarker = now;
|
|
100
|
+
}
|
|
101
|
+
const timeElapsed = now - this.config._Tmarker;
|
|
102
|
+
const timeRemaining = windowMs - timeElapsed;
|
|
103
|
+
// If we've hit request limit but window hasn't expired, queue and wait
|
|
104
|
+
if (this.config._count >= this.config.request && timeRemaining > 0) {
|
|
105
|
+
this.queue.push(input);
|
|
106
|
+
return {
|
|
107
|
+
status: 'queued',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// If window has expired, reset and proceed
|
|
111
|
+
if (timeRemaining <= 0) {
|
|
112
|
+
this.config._count = 0;
|
|
113
|
+
this.config._Tmarker = now;
|
|
114
|
+
}
|
|
115
|
+
// We can make the request
|
|
116
|
+
this.config._count++;
|
|
117
|
+
// If this request hits the limit, schedule reset after remaining time
|
|
118
|
+
if (this.config._count === this.config.request && timeRemaining > 0) {
|
|
119
|
+
const waitSeconds = timeRemaining / 1000;
|
|
120
|
+
await time.waitFor(async () => {
|
|
121
|
+
this.config._count = 0;
|
|
122
|
+
await this.processQueue();
|
|
123
|
+
}, waitSeconds);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const response = await this.config.cb(input);
|
|
127
|
+
this.eventManager.emit('completed', {
|
|
128
|
+
request: input,
|
|
129
|
+
response
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
status: 'success',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
status: 'error',
|
|
138
|
+
message: error.message
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async invoke(input) {
|
|
143
|
+
let response = null;
|
|
144
|
+
try {
|
|
145
|
+
if (this.config.mode === 'spread') {
|
|
146
|
+
response = await this.spreadAlgorithm(input);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
response = await this.burstAlgorithm(input);
|
|
150
|
+
}
|
|
151
|
+
return response;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
status: 'error',
|
|
156
|
+
message: error.message
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=ratelimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.js","sourceRoot":"","sources":["../src/ratelimit.ts"],"names":[],"mappings":"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"}
|
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":""}
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Cluster } from "./cluster";
|
|
2
|
+
const cluster = new Cluster('roundrobin');
|
|
3
|
+
cluster.addOption({
|
|
4
|
+
id: 'deepseek',
|
|
5
|
+
request: 100,
|
|
6
|
+
per: 'minute',
|
|
7
|
+
mode: 'spread',
|
|
8
|
+
cb: async (input) => {
|
|
9
|
+
return `Processed: ${input.modelId} - ${input.chatId} by Deepseek`;
|
|
10
|
+
}
|
|
11
|
+
}, true);
|
|
12
|
+
cluster.on('completed', (data) => {
|
|
13
|
+
console.log('Completed:', data.response);
|
|
14
|
+
});
|
|
15
|
+
cluster.on('error', (data) => {
|
|
16
|
+
console.error('Error processing:', data.request, 'Error:', data.response);
|
|
17
|
+
});
|
|
18
|
+
for (let i = 0; i < 500; i++) {
|
|
19
|
+
await cluster.invoke({
|
|
20
|
+
modelId: 'gpt-4',
|
|
21
|
+
chatId: `chat-${i}`,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=test.js.map
|
package/dist/test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,YAAY,CAAC,CAAC;AAEhD,OAAO,CAAC,SAAS,CAAC;IACd,EAAE,EAAE,UAAU;IACd,OAAO,EAAE,GAAG;IACZ,GAAG,EAAE,QAAQ;IACb,IAAI,EAAE,QAAQ;IACd,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAChB,OAAO,cAAc,KAAK,CAAC,OAAO,MAAM,KAAK,CAAC,MAAM,cAAc,CAAC;IACvE,CAAC;CACJ,EAAE,IAAI,CAAC,CAAC;AAET,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE;IAC7B,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;IACzB,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC;AAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3B,MAAM,OAAO,CAAC,MAAM,CAAC;QACjB,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,QAAQ,CAAC,EAAE;KACtB,CAAC,CAAC;AACP,CAAC"}
|
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class Interval {
|
|
2
|
+
callbacks: any[];
|
|
3
|
+
constructor();
|
|
4
|
+
add: (cb: (...args: any[]) => Promise<any>, timeout: number) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare class TimeCluster {
|
|
7
|
+
intervals: Interval[];
|
|
8
|
+
constructor(size: number);
|
|
9
|
+
waitFor: (cb: (...args: any[]) => Promise<any>, timeout: number) => void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAQ;IACjB,SAAS,EAAE,GAAG,EAAE,CAAM;;IAetB,GAAG,GAAI,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,UAG3D;CACJ;AAED,qBAAa,WAAW;IAEpB,SAAS,aAAyB;gBAEtB,IAAI,EAAE,MAAM;IAMxB,OAAO,GAAI,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,UAG/D;CACJ"}
|
package/dist/time.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class Interval {
|
|
2
|
+
callbacks = [];
|
|
3
|
+
constructor() {
|
|
4
|
+
setInterval(async () => {
|
|
5
|
+
const time = Date.now();
|
|
6
|
+
for (let i = this.callbacks.length - 1; i >= 0; i--) {
|
|
7
|
+
const item = this.callbacks[i];
|
|
8
|
+
if (time >= item.target) {
|
|
9
|
+
await item.cb();
|
|
10
|
+
this.callbacks.splice(i, 1);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}, 250);
|
|
14
|
+
}
|
|
15
|
+
add = (cb, timeout) => {
|
|
16
|
+
const target = Date.now() + timeout * 1000;
|
|
17
|
+
this.callbacks.push({ cb, target });
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export class TimeCluster {
|
|
21
|
+
intervals = new Array();
|
|
22
|
+
constructor(size) {
|
|
23
|
+
for (let i = 0; i < size; i++) {
|
|
24
|
+
this.intervals.push(new Interval());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
waitFor = (cb, timeout) => {
|
|
28
|
+
const interval = this.intervals[Math.floor(Math.random() * this.intervals.length)];
|
|
29
|
+
interval.add(cb, timeout);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=time.js.map
|
package/dist/time.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,QAAQ;IACjB,SAAS,GAAU,EAAE,CAAC;IAEtB;QACI,WAAW,CAAC,KAAK,IAAI,EAAE;YACnB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACxB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAClD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC/B,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBACtB,MAAM,IAAI,CAAC,EAAE,EAAE,CAAC;oBAChB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChC,CAAC;YACL,CAAC;QACL,CAAC,EAAE,GAAG,CAAC,CAAC;IACZ,CAAC;IAED,GAAG,GAAG,CAAC,EAAoC,EAAE,OAAe,EAAE,EAAE;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC;QAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,CAAC,CAAA;CACJ;AAED,MAAM,OAAO,WAAW;IAEpB,SAAS,GAAG,IAAI,KAAK,EAAY,CAAC;IAElC,YAAY,IAAY;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC;QACxC,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC,EAAoC,EAAE,OAAe,EAAE,EAAE;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACnF,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAA;CACJ"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ttc-rate-limit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A rate limiting library with worker pool support for parallel timeout management",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"private": false,
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"test": "bun test",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"rate-limit",
|
|
21
|
+
"rate-limiting",
|
|
22
|
+
"worker-pool",
|
|
23
|
+
"parallel",
|
|
24
|
+
"timeout",
|
|
25
|
+
"ttc"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "latest",
|
|
31
|
+
"@types/node": "^25.2.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|