{"version":3,"file":"index.cjs","names":["createClient"],"sources":["../src/EntrolyticsRedisClient.ts","../src/rate-limit.ts"],"sourcesContent":["import debug, { type Debugger } from 'debug';\nimport { createClient, type RedisClientType } from 'redis';\n\nexport const log: Debugger = debug('entrolytics:redis-client');\n\nexport const DELETED = '__DELETED__';\nexport const CACHE_PREFIX = 'entrolytics:';\n\nconst logError = (err: unknown) => log('Redis error:', err);\n\nexport interface EntrolyticsRedisClientOptions {\n  url: string;\n  prefix?: string;\n  defaultTTL?: number;\n  /** Max reconnection attempts before giving up */\n  maxReconnectAttempts?: number;\n  /** Connection timeout in milliseconds */\n  connectTimeout?: number;\n}\n\nexport interface CacheStats {\n  hits: number;\n  misses: number;\n  hitRate: number;\n}\n\n/** TTL options - accepts number (seconds) or redis-style options object */\nexport type TTLOption = number | { EX?: number; PX?: number; EXAT?: number; PXAT?: number };\n\nexport class EntrolyticsRedisClient {\n  url: string;\n  private redisClient: RedisClientType;\n  private connected: boolean;\n  private connecting: boolean;\n  private connectionPromise: Promise<void> | null;\n  prefix: string;\n  defaultTTL: number;\n  private stats: { hits: number; misses: number };\n\n  constructor({\n    url,\n    prefix = CACHE_PREFIX,\n    defaultTTL = 3600,\n    maxReconnectAttempts = 10,\n    connectTimeout = 10000,\n  }: EntrolyticsRedisClientOptions) {\n    const client = createClient({\n      url,\n      socket: {\n        connectTimeout,\n        reconnectStrategy: (retries, cause) => {\n          if (retries > maxReconnectAttempts) {\n            log(`Max reconnect attempts (${maxReconnectAttempts}) exceeded:`, cause?.message);\n            return new Error(`Max reconnect attempts exceeded: ${cause?.message}`);\n          }\n          // Exponential backoff with jitter\n          const jitter = Math.floor(Math.random() * 200);\n          const delay = Math.min(2 ** retries * 50, 3000);\n          log(`Reconnecting in ${delay + jitter}ms (attempt ${retries})`);\n          return delay + jitter;\n        },\n      },\n    });\n\n    client.on('error', logError);\n    client.on('connect', () => log('Redis connecting...'));\n    client.on('ready', () => {\n      log('Redis ready');\n      this.connected = true;\n    });\n    client.on('end', () => {\n      log('Redis connection ended');\n      this.connected = false;\n    });\n\n    this.url = url;\n    this.redisClient = client;\n    this.connected = false;\n    this.connecting = false;\n    this.connectionPromise = null;\n    this.prefix = prefix;\n    this.defaultTTL = defaultTTL;\n    this.stats = { hits: 0, misses: 0 };\n  }\n\n  /** Access the underlying redis client for advanced operations */\n  get client(): RedisClientType {\n    return this.redisClient;\n  }\n\n  /** Check if currently connected */\n  get isConnected(): boolean {\n    return this.connected;\n  }\n\n  private prefixKey(key: string): string {\n    return `${this.prefix}${key}`;\n  }\n\n  /** Extract TTL in seconds from TTLOption */\n  private extractTTL(ttl?: TTLOption): number | undefined {\n    if (ttl === undefined || ttl === null) return undefined;\n    if (typeof ttl === 'number') return ttl;\n    // Redis-style options object\n    if (ttl.EX !== undefined) return ttl.EX;\n    if (ttl.PX !== undefined) return Math.ceil(ttl.PX / 1000);\n    if (ttl.EXAT !== undefined) return Math.max(0, ttl.EXAT - Math.floor(Date.now() / 1000));\n    if (ttl.PXAT !== undefined) return Math.max(0, Math.ceil((ttl.PXAT - Date.now()) / 1000));\n    return undefined;\n  }\n\n  async connect(): Promise<void> {\n    // Already connected\n    if (this.connected) return;\n\n    // Connection in progress - wait for it\n    if (this.connecting && this.connectionPromise) {\n      await this.connectionPromise;\n      return;\n    }\n\n    // Start new connection\n    this.connecting = true;\n    this.connectionPromise = this.redisClient.connect().then(\n      () => {\n        this.connected = true;\n        this.connecting = false;\n        log('Redis connected');\n      },\n      err => {\n        this.connecting = false;\n        this.connectionPromise = null;\n        throw err;\n      },\n    );\n\n    await this.connectionPromise;\n  }\n\n  async disconnect(): Promise<void> {\n    if (this.connected) {\n      await this.redisClient.disconnect();\n      this.connected = false;\n      this.connecting = false;\n      this.connectionPromise = null;\n      log('Redis disconnected');\n    }\n  }\n\n  async get<T = unknown>(key: string): Promise<T | null> {\n    try {\n      await this.connect();\n      const data = await this.redisClient.get(this.prefixKey(key));\n\n      if (data === null) return null;\n\n      try {\n        // oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion\n        return JSON.parse(data) as T;\n      } catch {\n        // If JSON parse fails, return as-is (might be a plain string)\n        // oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion\n        return data as T;\n      }\n    } catch (err) {\n      log('Redis get error:', err);\n      return null;\n    }\n  }\n\n  /**\n   * Get raw string value without JSON parsing\n   */\n  async getString(key: string): Promise<string | null> {\n    try {\n      await this.connect();\n      return this.redisClient.get(this.prefixKey(key));\n    } catch (err) {\n      log('Redis getString error:', err);\n      return null;\n    }\n  }\n\n  /**\n   * Get multiple values at once\n   */\n  async mGet(keys: string[]): Promise<(string | null)[]> {\n    try {\n      await this.connect();\n      const prefixedKeys = keys.map(k => this.prefixKey(k));\n      return this.redisClient.mGet(prefixedKeys);\n    } catch (err) {\n      log('Redis mGet error:', err);\n      return keys.map(() => null);\n    }\n  }\n\n  /**\n   * Set with expiry (convenience method for setEx pattern)\n   */\n  async setEx(key: string, seconds: number, value: string): Promise<string | null> {\n    try {\n      await this.connect();\n      return this.redisClient.setEx(this.prefixKey(key), seconds, value);\n    } catch (err) {\n      log('Redis setEx error:', err);\n      return null;\n    }\n  }\n\n  /**\n   * Set a value in cache\n   * @param key - Cache key\n   * @param value - Value to cache (will be JSON serialized)\n   * @param ttl - TTL as number (seconds) or options object { EX?: number, PX?: number }\n   */\n  async set(key: string, value: unknown, ttl?: TTLOption): Promise<string | null> {\n    try {\n      await this.connect();\n\n      const serialized = JSON.stringify(value);\n      const prefixedKey = this.prefixKey(key);\n      const ttlSeconds = this.extractTTL(ttl) ?? this.defaultTTL;\n\n      if (ttlSeconds > 0) {\n        return this.redisClient.setEx(prefixedKey, ttlSeconds, serialized);\n      }\n\n      return this.redisClient.set(prefixedKey, serialized);\n    } catch (err) {\n      log('Redis set error:', err);\n      return null;\n    }\n  }\n\n  async del(key: string): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.del(this.prefixKey(key));\n    } catch (err) {\n      log('Redis del error:', err);\n      return 0;\n    }\n  }\n\n  async incr(key: string): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.incr(this.prefixKey(key));\n    } catch (err) {\n      log('Redis incr error:', err);\n      return 0;\n    }\n  }\n\n  async incrBy(key: string, amount: number): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.incrBy(this.prefixKey(key), amount);\n    } catch (err) {\n      log('Redis incrBy error:', err);\n      return 0;\n    }\n  }\n\n  async decr(key: string): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.decr(this.prefixKey(key));\n    } catch (err) {\n      log('Redis decr error:', err);\n      return 0;\n    }\n  }\n\n  async decrBy(key: string, amount: number): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.decrBy(this.prefixKey(key), amount);\n    } catch (err) {\n      log('Redis decrBy error:', err);\n      return 0;\n    }\n  }\n\n  async expire(key: string, seconds: number): Promise<boolean> {\n    try {\n      await this.connect();\n      const result = await this.redisClient.expire(this.prefixKey(key), seconds);\n      return result === 1;\n    } catch (err) {\n      log('Redis expire error:', err);\n      return false;\n    }\n  }\n\n  async ttl(key: string): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.ttl(this.prefixKey(key));\n    } catch (err) {\n      log('Redis ttl error:', err);\n      return -1;\n    }\n  }\n\n  async exists(key: string): Promise<boolean> {\n    try {\n      await this.connect();\n      const result = await this.redisClient.exists(this.prefixKey(key));\n      return result === 1;\n    } catch (err) {\n      log('Redis exists error:', err);\n      return false;\n    }\n  }\n\n  // Sorted Set operations (for sliding window rate limiting)\n\n  async zAdd(key: string, score: number, value: string): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.zAdd(this.prefixKey(key), { score, value });\n    } catch (err) {\n      log('Redis zAdd error:', err);\n      return 0;\n    }\n  }\n\n  async zRemRangeByScore(key: string, min: number, max: number): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.zRemRangeByScore(this.prefixKey(key), min, max);\n    } catch (err) {\n      log('Redis zRemRangeByScore error:', err);\n      return 0;\n    }\n  }\n\n  async zCard(key: string): Promise<number> {\n    try {\n      await this.connect();\n      return this.redisClient.zCard(this.prefixKey(key));\n    } catch (err) {\n      log('Redis zCard error:', err);\n      return 0;\n    }\n  }\n\n  async zRange(key: string, start: number, stop: number): Promise<string[]> {\n    try {\n      await this.connect();\n      return this.redisClient.zRange(this.prefixKey(key), start, stop);\n    } catch (err) {\n      log('Redis zRange error:', err);\n      return [];\n    }\n  }\n\n  /**\n   * Rate limiting helper\n   * Returns true if the rate limit has been exceeded\n   */\n  async rateLimit(key: string, limit: number, windowSeconds: number): Promise<boolean> {\n    try {\n      await this.connect();\n\n      const prefixedKey = this.prefixKey(`ratelimit:${key}`);\n      const current = await this.redisClient.incr(prefixedKey);\n\n      if (current === 1) {\n        await this.redisClient.expire(prefixedKey, windowSeconds);\n      }\n\n      return current > limit;\n    } catch (err) {\n      log('Redis rateLimit error:', err);\n      return false; // Fail open - allow the request if Redis is down\n    }\n  }\n\n  /**\n   * Get remaining rate limit count\n   */\n  async getRateLimitRemaining(key: string, limit: number): Promise<number> {\n    try {\n      await this.connect();\n\n      const prefixedKey = this.prefixKey(`ratelimit:${key}`);\n      const current = await this.redisClient.get(prefixedKey);\n      const count = current ? parseInt(current, 10) : 0;\n\n      return Math.max(0, limit - count);\n    } catch (err) {\n      log('Redis getRateLimitRemaining error:', err);\n      return limit; // Return full limit if Redis is down\n    }\n  }\n\n  /**\n   * Cache-through pattern: fetch from cache or execute query and cache result\n   */\n  async fetch<T>(key: string, query: () => Promise<T>, ttl?: number): Promise<T | null> {\n    const result = await this.get<T>(key);\n\n    if (result === DELETED) {\n      this.stats.hits++;\n      return null;\n    }\n\n    if (result !== null) {\n      this.stats.hits++;\n      return result;\n    }\n\n    this.stats.misses++;\n\n    if (query) {\n      const data = await query();\n\n      if (data !== null && data !== undefined) {\n        await this.store(key, data, ttl);\n      }\n\n      return data;\n    }\n\n    return null;\n  }\n\n  /**\n   * Store data in cache with optional TTL\n   */\n  async store(key: string, data: unknown, ttl?: number): Promise<string | null> {\n    return this.set(key, data, ttl);\n  }\n\n  /**\n   * Remove from cache - soft delete marks as deleted, hard delete removes entirely\n   */\n  async remove(key: string, soft = false): Promise<string | number | null> {\n    return soft ? this.set(key, DELETED) : this.del(key);\n  }\n\n  /**\n   * Invalidate multiple keys by pattern using SCAN (production-safe, non-blocking)\n   */\n  async invalidatePattern(pattern: string): Promise<number> {\n    try {\n      await this.connect();\n\n      const prefixedPattern = this.prefixKey(pattern);\n      let cursor: string = '0';\n      let deletedCount = 0;\n\n      do {\n        const result = await this.redisClient.scan(cursor, {\n          MATCH: prefixedPattern,\n          COUNT: 100,\n        });\n        cursor = result.cursor;\n\n        if (result.keys.length > 0) {\n          deletedCount += await this.redisClient.del(result.keys);\n        }\n      } while (cursor !== '0');\n\n      return deletedCount;\n    } catch (err) {\n      log('Redis invalidatePattern error:', err);\n      return 0;\n    }\n  }\n\n  /**\n   * Get cache statistics\n   */\n  getStats(): CacheStats {\n    const total = this.stats.hits + this.stats.misses;\n    return {\n      hits: this.stats.hits,\n      misses: this.stats.misses,\n      hitRate: total > 0 ? this.stats.hits / total : 0,\n    };\n  }\n\n  /**\n   * Reset cache statistics\n   */\n  resetStats(): void {\n    this.stats = { hits: 0, misses: 0 };\n  }\n\n  /**\n   * Ping Redis to check connection\n   */\n  async ping(): Promise<string> {\n    await this.connect();\n    return this.redisClient.ping();\n  }\n\n  /**\n   * Health check for Redis connection\n   */\n  async healthCheck(): Promise<{ ok: boolean; latency: number; error?: string }> {\n    const start = Date.now();\n    try {\n      await this.ping();\n      return { ok: true, latency: Date.now() - start };\n    } catch (error) {\n      return {\n        ok: false,\n        latency: Date.now() - start,\n        error: error instanceof Error ? error.message : 'Unknown error',\n      };\n    }\n  }\n\n  /**\n   * Flush all keys with the configured prefix\n   */\n  async flushPrefix(): Promise<number> {\n    return this.invalidatePattern('*');\n  }\n}\n\nexport default EntrolyticsRedisClient;\n","/**\n * Rate limiting utilities with sliding window algorithm\n * Supports Redis (distributed) and in-memory (single instance) fallback\n */\n\nimport type { EntrolyticsRedisClient } from './EntrolyticsRedisClient';\nimport { log } from './EntrolyticsRedisClient';\n\nexport interface RateLimitConfig {\n  /**\n   * Unique identifier for this rate limit bucket\n   */\n  bucket: string;\n\n  /**\n   * Maximum number of requests allowed\n   */\n  limit: number;\n\n  /**\n   * Time window in seconds\n   */\n  window: number;\n}\n\nexport interface RateLimitResult {\n  success: boolean;\n  limit: number;\n  remaining: number;\n  reset: number;\n}\n\n/**\n * Predefined rate limit configurations for Entrolytics\n */\nexport const RATE_LIMITS = {\n  // Strict limits for security-sensitive endpoints\n  CLI_TOKEN_EXCHANGE: {\n    bucket: 'cli:token',\n    limit: 5,\n    window: 60, // 5 requests per minute\n  },\n\n  SHARE_TOKEN_GENERATION: {\n    bucket: 'share:token',\n    limit: 10,\n    window: 60, // 10 requests per minute\n  },\n\n  LINK_CREATION: {\n    bucket: 'link:create',\n    limit: 20,\n    window: 60, // 20 links per minute\n  },\n\n  // Medium limits for tracking/redirect\n  LINK_REDIRECT: {\n    bucket: 'link:redirect',\n    limit: 100,\n    window: 60, // 100 redirects per minute per IP\n  },\n\n  // Event ingestion rate limits (per website)\n  EVENT_INGESTION: {\n    bucket: 'event:ingest',\n    limit: 1000,\n    window: 60, // 1000 events per minute per website\n  },\n\n  // Pixel tracking rate limits (per pixel)\n  PIXEL_FIRE: {\n    bucket: 'pixel:fire',\n    limit: 500,\n    window: 60, // 500 pixel fires per minute per pixel\n  },\n\n  // General API limits\n  API_GENERAL: {\n    bucket: 'api:general',\n    limit: 100,\n    window: 60, // 100 requests per minute\n  },\n} as const;\n\n/**\n * In-memory rate limiting store (fallback when Redis is not available)\n * Note: This only works for single instance deployments\n */\nconst rateLimitStore = new Map<string, number[]>();\n\n/**\n * Clean up old entries from in-memory store\n */\nfunction cleanupRateLimitStore(threshold: number) {\n  for (const [key, timestamps] of rateLimitStore.entries()) {\n    const filtered = timestamps.filter(ts => ts > threshold);\n    if (filtered.length === 0) {\n      rateLimitStore.delete(key);\n    } else {\n      rateLimitStore.set(key, filtered);\n    }\n  }\n}\n\n/**\n * In-memory rate limiting (single instance only)\n */\nfunction inMemoryRateLimit(\n  key: string,\n  config: RateLimitConfig,\n  now: number,\n  windowStart: number,\n): RateLimitResult {\n  // Get or create bucket\n  let timestamps = rateLimitStore.get(key) || [];\n\n  // Remove old entries\n  timestamps = timestamps.filter(ts => ts > windowStart);\n\n  // Check limit\n  if (timestamps.length >= config.limit) {\n    rateLimitStore.set(key, timestamps);\n\n    return {\n      success: false,\n      limit: config.limit,\n      remaining: 0,\n      reset: now + config.window,\n    };\n  }\n\n  // Add current request\n  timestamps.push(now);\n  rateLimitStore.set(key, timestamps);\n\n  // Clean up old keys periodically (basic memory management)\n  if (Math.random() < 0.01) {\n    // 1% chance\n    cleanupRateLimitStore(windowStart);\n  }\n\n  return {\n    success: true,\n    limit: config.limit,\n    remaining: config.limit - timestamps.length,\n    reset: now + config.window,\n  };\n}\n\n/**\n * Rate limiting using sliding window algorithm\n *\n * Uses Redis if client provided (distributed rate limiting), falls back to\n * in-memory Map (single instance) if client is null.\n *\n * @param client - Redis client instance or null for in-memory fallback\n * @param identifier - Unique identifier (e.g., IP address, user ID, website ID)\n * @param config - Rate limit configuration\n */\nexport async function rateLimit(\n  client: EntrolyticsRedisClient | null,\n  identifier: string,\n  config: RateLimitConfig,\n): Promise<RateLimitResult> {\n  const key = `ratelimit:${config.bucket}:${identifier}`;\n  const now = Math.floor(Date.now() / 1000);\n  const windowStart = now - config.window;\n\n  if (client?.isConnected) {\n    try {\n      // Use Redis for distributed rate limiting with sliding window\n      const redis = client.client;\n      const prefixedKey = `${client.prefix}${key}`;\n\n      const multi = redis.multi();\n\n      // Remove old entries outside the window\n      multi.zRemRangeByScore(prefixedKey, 0, windowStart);\n\n      // Count current requests in window\n      multi.zCard(prefixedKey);\n\n      // Add current request with unique value\n      multi.zAdd(prefixedKey, { score: now, value: `${now}:${Math.random()}` });\n\n      // Set expiry on the key\n      multi.expire(prefixedKey, config.window);\n\n      const results = await multi.exec();\n\n      // Results: [removeResult, countResult, addResult, expireResult]\n      const count = Number(results?.[1]) || 0;\n\n      const remaining = Math.max(0, config.limit - count - 1);\n      const reset = now + config.window;\n\n      if (count >= config.limit) {\n        return {\n          success: false,\n          limit: config.limit,\n          remaining: 0,\n          reset,\n        };\n      }\n\n      return {\n        success: true,\n        limit: config.limit,\n        remaining,\n        reset,\n      };\n    } catch (err) {\n      log('Redis rate limiting error, falling back to in-memory:', err);\n    }\n  }\n\n  // Fallback to in-memory rate limiting (single instance only)\n  return inMemoryRateLimit(key, config, now, windowStart);\n}\n\n/**\n * Simple counter-based rate limiting (less accurate but simpler)\n * Uses INCR with expiry - good for high-volume, less precise scenarios\n *\n * @param client - Redis client instance\n * @param identifier - Unique identifier\n * @param limit - Maximum requests allowed\n * @param windowSeconds - Time window in seconds\n * @returns true if rate limit exceeded, false otherwise\n */\nexport async function rateLimitSimple(\n  client: EntrolyticsRedisClient | null,\n  identifier: string,\n  limit: number,\n  windowSeconds: number,\n): Promise<boolean> {\n  if (!client) {\n    // In-memory fallback for simple rate limiting\n    const key = `simple:${identifier}`;\n    const now = Math.floor(Date.now() / 1000);\n    const windowStart = now - windowSeconds;\n\n    let timestamps = rateLimitStore.get(key) || [];\n    timestamps = timestamps.filter(ts => ts > windowStart);\n\n    if (timestamps.length >= limit) {\n      return true; // Rate limited\n    }\n\n    timestamps.push(now);\n    rateLimitStore.set(key, timestamps);\n    return false;\n  }\n\n  return client.rateLimit(identifier, limit, windowSeconds);\n}\n\n/**\n * Get rate limit headers for HTTP response\n */\nexport function getRateLimitHeaders(result: RateLimitResult): Record<string, string> {\n  return {\n    'X-RateLimit-Limit': String(result.limit),\n    'X-RateLimit-Remaining': String(result.remaining),\n    'X-RateLimit-Reset': String(result.reset),\n    ...(result.success\n      ? {}\n      : { 'Retry-After': String(result.reset - Math.floor(Date.now() / 1000)) }),\n  };\n}\n\n/**\n * Create a rate limiter bound to a specific Redis client\n * Useful for creating app-specific rate limiters\n */\nexport interface RateLimiter {\n  check: (identifier: string, config: RateLimitConfig) => Promise<RateLimitResult>\n  checkSimple: (identifier: string, limit: number, windowSeconds: number) => Promise<boolean>\n  getRemaining: (identifier: string, config: RateLimitConfig) => Promise<number>\n}\n\nexport function createRateLimiter(client: EntrolyticsRedisClient | null): RateLimiter {\n  return {\n    /**\n     * Check rate limit for an identifier\n     */\n    check: (identifier: string, config: RateLimitConfig) => rateLimit(client, identifier, config),\n\n    /**\n     * Simple counter-based rate limit check\n     */\n    checkSimple: (identifier: string, limit: number, windowSeconds: number) =>\n      rateLimitSimple(client, identifier, limit, windowSeconds),\n\n    /**\n     * Get remaining requests for an identifier\n     */\n    getRemaining: async (identifier: string, config: RateLimitConfig): Promise<number> => {\n      if (!client) {\n        const key = `ratelimit:${config.bucket}:${identifier}`;\n        const timestamps = rateLimitStore.get(key) || [];\n        const windowStart = Math.floor(Date.now() / 1000) - config.window;\n        const validTimestamps = timestamps.filter(ts => ts > windowStart);\n        return Math.max(0, config.limit - validTimestamps.length);\n      }\n\n      return client.getRateLimitRemaining(`${config.bucket}:${identifier}`, config.limit);\n    },\n  };\n}\n"],"mappings":"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"}