okta-mcp-server
Version:
Model Context Protocol (MCP) server for Okta API operations with support for bulk operations and caching
83 lines • 2.45 kB
JavaScript
/**
* Token bucket rate limiter implementation
*/
export class TokenBucketRateLimiter {
buckets = new Map();
capacity;
refillRate;
window;
constructor(options) {
this.capacity = options.max;
this.window = options.window;
this.refillRate = options.max / (options.window / 1000); // tokens per second
}
getBucket(key) {
let bucket = this.buckets.get(key);
if (!bucket) {
bucket = {
tokens: this.capacity,
lastRefill: Date.now(),
capacity: this.capacity,
refillRate: this.refillRate,
};
this.buckets.set(key, bucket);
}
return bucket;
}
refillBucket(bucket) {
const now = Date.now();
const timePassed = (now - bucket.lastRefill) / 1000; // seconds
const tokensToAdd = timePassed * bucket.refillRate;
bucket.tokens = Math.min(bucket.capacity, bucket.tokens + tokensToAdd);
bucket.lastRefill = now;
}
async check(key) {
const bucket = this.getBucket(key);
this.refillBucket(bucket);
return {
allowed: bucket.tokens >= 1,
remaining: Math.floor(bucket.tokens),
reset: Date.now() + this.window,
limit: this.capacity,
};
}
async consume(key, tokens = 1) {
const bucket = this.getBucket(key);
this.refillBucket(bucket);
const allowed = bucket.tokens >= tokens;
if (allowed) {
bucket.tokens -= tokens;
}
return {
allowed,
remaining: Math.max(0, Math.floor(bucket.tokens)),
reset: Date.now() + this.window,
limit: this.capacity,
};
}
async reset(key) {
this.buckets.delete(key);
}
async status(key) {
return this.check(key);
}
/**
* Clean up old buckets (for memory management)
*/
cleanup() {
const now = Date.now();
const maxAge = this.window * 2; // Keep buckets for 2x the window
for (const [key, bucket] of this.buckets) {
if (now - bucket.lastRefill > maxAge) {
this.buckets.delete(key);
}
}
}
/**
* Start periodic cleanup
*/
startCleanup(interval = 60000) {
return setInterval(() => this.cleanup(), interval);
}
}
//# sourceMappingURL=token-bucket.js.map