// src/core/constants.ts
var RATE_LIMIT = {
  /** Default window size (1 minute) */
  DEFAULT_WINDOW_MS: 6e4,
  /** Minimum window size (1 second) */
  MIN_WINDOW_MS: 1e3};

// src/stores/memory.ts
var DEFAULT_MAX_SIZE = 1e4;
var MemoryStore = class {
  constructor(windowMs = RATE_LIMIT.DEFAULT_WINDOW_MS, maxSize = DEFAULT_MAX_SIZE) {
    this.store = /* @__PURE__ */ new Map();
    this.cleanupInterval = null;
    if (!Number.isFinite(windowMs) || windowMs < RATE_LIMIT.MIN_WINDOW_MS) {
      throw new RangeError(
        `MemoryStore: windowMs must be a finite number >= ${RATE_LIMIT.MIN_WINDOW_MS} (got ${windowMs})`
      );
    }
    if (!Number.isFinite(maxSize) || maxSize < 1) {
      throw new RangeError(`MemoryStore: maxSize must be >= 1 (got ${maxSize})`);
    }
    this.windowMs = windowMs;
    this.maxSize = maxSize;
    this.startCleanup();
  }
  /**
   * Start the cleanup interval to remove expired entries.
   */
  startCleanup() {
    const CLEANUP_MIN_MS = 3e4;
    const CLEANUP_MAX_MS = 3e5;
    const cleanupMs = Math.min(Math.max(this.windowMs, CLEANUP_MIN_MS), CLEANUP_MAX_MS);
    this.cleanupInterval = setInterval(() => {
      const now = Date.now();
      for (const [key, entry] of this.store.entries()) {
        if (entry.resetTime < now) {
          this.store.delete(key);
        }
      }
    }, cleanupMs);
    if (typeof this.cleanupInterval.unref === "function") {
      this.cleanupInterval.unref();
    }
  }
  async get(key) {
    const entry = this.store.get(key);
    if (!entry) return null;
    if (entry.resetTime < Date.now()) {
      this.store.delete(key);
      return null;
    }
    return entry;
  }
  async set(key, entry) {
    if (!this.store.has(key) && this.store.size >= this.maxSize) {
      this.evictExpired();
      if (this.store.size >= this.maxSize) return;
    }
    this.store.set(key, entry);
  }
  async increment(key) {
    const now = Date.now();
    const entry = this.store.get(key);
    if (!entry || entry.resetTime < now) {
      if (this.store.size >= this.maxSize) {
        this.evictExpired();
        if (this.store.size >= this.maxSize) return 1;
      }
      this.store.set(key, { count: 1, resetTime: now + this.windowMs });
      return 1;
    }
    entry.count++;
    return entry.count;
  }
  /** Eagerly remove expired entries to reclaim capacity. */
  evictExpired() {
    const now = Date.now();
    for (const [key, entry] of this.store.entries()) {
      if (entry.resetTime < now) this.store.delete(key);
    }
  }
  async decrement(key) {
    const entry = this.store.get(key);
    if (entry && entry.count > 0) {
      entry.count--;
    }
  }
  async reset(key) {
    this.store.delete(key);
  }
  async close() {
    if (this.cleanupInterval) {
      clearInterval(this.cleanupInterval);
      this.cleanupInterval = null;
    }
    this.store.clear();
  }
  /**
   * Get current store size (for monitoring).
   */
  get size() {
    return this.store.size;
  }
};

// src/stores/redis.ts
var RedisStore = class {
  constructor(options) {
    this.client = options.client;
    this.prefix = options.prefix ?? "arcis:rl:";
    this.windowMs = options.windowMs ?? RATE_LIMIT.DEFAULT_WINDOW_MS;
    this.windowSec = Math.ceil(this.windowMs / 1e3);
  }
  getKey(key) {
    return `${this.prefix}${key}`;
  }
  async get(key) {
    const redisKey = this.getKey(key);
    const [countStr, ttl] = await Promise.all([
      this.client.get(redisKey),
      this.client.ttl(redisKey)
    ]);
    if (!countStr || ttl < 0) {
      return null;
    }
    const count = parseInt(countStr, 10);
    if (isNaN(count)) {
      return null;
    }
    return {
      count,
      resetTime: Date.now() + ttl * 1e3
    };
  }
  async set(key, entry) {
    const redisKey = this.getKey(key);
    const ttlSec = Math.max(1, Math.ceil((entry.resetTime - Date.now()) / 1e3));
    await this.client.setex(redisKey, ttlSec, entry.count.toString());
  }
  async increment(key) {
    const redisKey = this.getKey(key);
    const created = await this.client.set(redisKey, "1", "EX", this.windowSec, "NX");
    if (created === "OK" || created === true) {
      return 1;
    }
    return this.client.incr(redisKey);
  }
  async decrement(key) {
    const redisKey = this.getKey(key);
    await this.client.decr(redisKey);
  }
  async reset(key) {
    const redisKey = this.getKey(key);
    await this.client.del(redisKey);
  }
  async close() {
  }
};
function createRedisStore(options) {
  return new RedisStore(options);
}

export { MemoryStore, RedisStore, createRedisStore };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map