@lara-node/cache 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Cache","CacheModel","getCache"],"sources":["../src/Models/Cache.ts","../src/RateLimiter.ts","../src/CacheManager.ts","../src/CacheServiceProvider.ts"],"sourcesContent":["import { Model } from \"@lara-node/db\";\n\nexport class Cache extends Model {\n static table = \"cache_store\";\n static primaryKey = \"k\";\n static autoIncrement = false;\n static fillable = [\"k\", \"v\", \"expires_at\"];\n static hidden: string[] = [];\n\n constructor(attributes: any = {}) {\n super(attributes);\n }\n}\n\nexport default Cache;\n","/**\n * Rate Limiter - Laravel-style rate limiting using cache drivers\n *\n * Supports multiple algorithms:\n * - Fixed Window: Simple counter reset after window expires\n * - Sliding Window: More accurate rate limiting using timestamps\n * - Token Bucket: Gradual replenishment of tokens\n *\n * Usage:\n * import { RateLimiter } from '@/cache/RateLimiter';\n *\n * // Check if too many attempts\n * const limiter = new RateLimiter();\n * if (await limiter.tooManyAttempts('login:user@example.com', 5, 60)) {\n * const retryAfter = await limiter.availableIn('login:user@example.com', 60);\n * throw new Error(`Too many attempts. Retry after ${retryAfter} seconds.`);\n * }\n * await limiter.hit('login:user@example.com', 60);\n */\n\nimport { Cache } from \"./index.js\";\n\nexport interface RateLimiterConfig {\n /** Prefix for rate limiter cache keys */\n prefix?: string;\n /** Default max attempts */\n maxAttempts?: number;\n /** Default decay time in seconds */\n decaySeconds?: number;\n}\n\nexport interface RateLimitInfo {\n /** Whether the rate limit has been exceeded */\n limited: boolean;\n /** Current number of attempts */\n attempts: number;\n /** Maximum allowed attempts */\n maxAttempts: number;\n /** Remaining attempts */\n remaining: number;\n /** Seconds until the rate limit resets */\n retryAfter: number;\n /** Timestamp when the rate limit resets (Unix timestamp in seconds) */\n resetsAt: number;\n}\n\n/**\n * Rate Limiter class using cache backend\n */\nexport class RateLimiter {\n private prefix: string;\n private defaultMaxAttempts: number;\n private defaultDecaySeconds: number;\n\n constructor(config: RateLimiterConfig = {}) {\n this.prefix = config.prefix ?? \"rate_limiter:\";\n this.defaultMaxAttempts = config.maxAttempts ?? 60;\n this.defaultDecaySeconds = config.decaySeconds ?? 60;\n }\n\n /**\n * Get the cache key for a given key\n */\n private cacheKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n /**\n * Get the timer cache key for a given key (stores the reset timestamp)\n */\n private timerKey(key: string): string {\n return `${this.prefix}${key}:timer`;\n }\n\n /**\n * Determine if the given key has been \"accessed\" too many times\n */\n async tooManyAttempts(\n key: string,\n maxAttempts?: number,\n _decaySeconds?: number,\n ): Promise<boolean> {\n const max = maxAttempts ?? this.defaultMaxAttempts;\n\n if ((await this.attempts(key)) >= max) {\n if (await Cache.has(this.timerKey(key))) {\n return true;\n }\n await this.resetAttempts(key);\n }\n\n return false;\n }\n\n /**\n * Increment the counter for a given key\n * Returns the new number of attempts\n */\n async hit(key: string, decaySeconds?: number): Promise<number> {\n const decay = decaySeconds ?? this.defaultDecaySeconds;\n const cKey = this.cacheKey(key);\n const tKey = this.timerKey(key);\n\n // Get current attempts\n const current = await this.attempts(key);\n const newAttempts = current + 1;\n\n // Check if timer exists\n const timerExists = await Cache.has(tKey);\n\n if (!timerExists) {\n // Set both the counter and timer\n const expiresAt = Math.floor(Date.now() / 1000) + decay;\n await Cache.set(tKey, expiresAt, decay);\n await Cache.set(cKey, newAttempts, decay);\n } else {\n // Only update the counter with remaining TTL\n const expiresAt = await Cache.get(tKey);\n const remainingTtl = expiresAt\n ? Math.max(1, expiresAt - Math.floor(Date.now() / 1000))\n : decay;\n await Cache.set(cKey, newAttempts, remainingTtl);\n }\n\n return newAttempts;\n }\n\n /**\n * Get the number of attempts for the given key\n */\n async attempts(key: string): Promise<number> {\n const val = await Cache.get(this.cacheKey(key));\n return typeof val === \"number\" ? val : parseInt(val, 10) || 0;\n }\n\n /**\n * Reset the number of attempts for the given key\n */\n async resetAttempts(key: string): Promise<boolean> {\n await Cache.del(this.cacheKey(key));\n await Cache.del(this.timerKey(key));\n return true;\n }\n\n /**\n * Get the number of retries remaining\n */\n async retriesLeft(key: string, maxAttempts?: number): Promise<number> {\n const max = maxAttempts ?? this.defaultMaxAttempts;\n const attempts = await this.attempts(key);\n return Math.max(0, max - attempts);\n }\n\n /**\n * Clear the hits and lockout timer for the given key\n */\n async clear(key: string): Promise<void> {\n await this.resetAttempts(key);\n }\n\n /**\n * Get the number of seconds until the key is accessible again\n */\n async availableIn(key: string, _decaySeconds?: number): Promise<number> {\n const expiresAt = await Cache.get(this.timerKey(key));\n\n if (!expiresAt) {\n return 0;\n }\n\n const now = Math.floor(Date.now() / 1000);\n return Math.max(0, expiresAt - now);\n }\n\n /**\n * Get the timestamp when the key becomes available again\n */\n async availableAt(key: string): Promise<number> {\n const expiresAt = await Cache.get(this.timerKey(key));\n return expiresAt || Math.floor(Date.now() / 1000);\n }\n\n /**\n * Attempt to execute a callback if the rate limit allows\n * Returns the callback result or throws if rate limited\n */\n async attempt<T>(\n key: string,\n maxAttempts: number,\n callback: () => T | Promise<T>,\n decaySeconds?: number,\n ): Promise<T> {\n const decay = decaySeconds ?? this.defaultDecaySeconds;\n\n if (await this.tooManyAttempts(key, maxAttempts, decay)) {\n const retryAfter = await this.availableIn(key, decay);\n throw new RateLimitExceededException(\n `Too many attempts. Please retry after ${retryAfter} seconds.`,\n retryAfter,\n maxAttempts,\n );\n }\n\n await this.hit(key, decay);\n return callback();\n }\n\n /**\n * Get complete rate limit info for a key\n */\n async getInfo(key: string, maxAttempts?: number, decaySeconds?: number): Promise<RateLimitInfo> {\n const max = maxAttempts ?? this.defaultMaxAttempts;\n const decay = decaySeconds ?? this.defaultDecaySeconds;\n const attempts = await this.attempts(key);\n const remaining = Math.max(0, max - attempts);\n const retryAfter = await this.availableIn(key, decay);\n const resetsAt = await this.availableAt(key);\n\n return {\n limited: attempts >= max && retryAfter > 0,\n attempts,\n maxAttempts: max,\n remaining,\n retryAfter,\n resetsAt,\n };\n }\n\n /**\n * Execute a callback with rate limiting, using a limiter definition\n */\n async limiter<T>(\n name: string,\n key: string,\n maxAttempts: number,\n decaySeconds: number,\n callback: () => T | Promise<T>,\n ): Promise<T> {\n const fullKey = `${name}:${key}`;\n return this.attempt(fullKey, maxAttempts, callback, decaySeconds);\n }\n}\n\n/**\n * Exception thrown when rate limit is exceeded\n */\nexport class RateLimitExceededException extends Error {\n public readonly retryAfter: number;\n public readonly maxAttempts: number;\n public readonly statusCode: number = 429;\n\n constructor(message: string, retryAfter: number, maxAttempts: number) {\n super(message);\n this.name = \"RateLimitExceededException\";\n this.retryAfter = retryAfter;\n this.maxAttempts = maxAttempts;\n }\n}\n\n// Named rate limiters registry (Laravel-style)\nconst namedLimiters: Map<string, () => { maxAttempts: number; decaySeconds: number }> = new Map();\n\n/**\n * Register a named rate limiter\n */\nexport function defineRateLimiter(\n name: string,\n config: () => { maxAttempts: number; decaySeconds: number },\n): void {\n namedLimiters.set(name, config);\n}\n\n/**\n * Get a named rate limiter configuration\n */\nexport function getNamedLimiter(\n name: string,\n): { maxAttempts: number; decaySeconds: number } | null {\n const limiter = namedLimiters.get(name);\n return limiter ? limiter() : null;\n}\n\n// Singleton instance for convenience\nconst rateLimiter = new RateLimiter();\n\n// Export facade-style methods\nexport const RateLimiterFacade = {\n /** Check if too many attempts have been made */\n tooManyAttempts: (key: string, maxAttempts?: number, decaySeconds?: number) =>\n rateLimiter.tooManyAttempts(key, maxAttempts, decaySeconds),\n\n /** Increment the attempt counter */\n hit: (key: string, decaySeconds?: number) => rateLimiter.hit(key, decaySeconds),\n\n /** Get current number of attempts */\n attempts: (key: string) => rateLimiter.attempts(key),\n\n /** Reset attempt counter */\n resetAttempts: (key: string) => rateLimiter.resetAttempts(key),\n\n /** Get remaining retries */\n retriesLeft: (key: string, maxAttempts?: number) => rateLimiter.retriesLeft(key, maxAttempts),\n\n /** Clear rate limiter for key */\n clear: (key: string) => rateLimiter.clear(key),\n\n /** Get seconds until rate limit resets */\n availableIn: (key: string, decaySeconds?: number) => rateLimiter.availableIn(key, decaySeconds),\n\n /** Get timestamp when rate limit resets */\n availableAt: (key: string) => rateLimiter.availableAt(key),\n\n /** Attempt to execute callback with rate limiting */\n attempt: <T>(\n key: string,\n maxAttempts: number,\n callback: () => T | Promise<T>,\n decaySeconds?: number,\n ) => rateLimiter.attempt(key, maxAttempts, callback, decaySeconds),\n\n /** Get rate limit info for a key */\n getInfo: (key: string, maxAttempts?: number, decaySeconds?: number) =>\n rateLimiter.getInfo(key, maxAttempts, decaySeconds),\n\n /** Define a named rate limiter */\n define: defineRateLimiter,\n\n /** Get named limiter config */\n limiter: getNamedLimiter,\n\n /** Execute with named limiter */\n for: async <T>(name: string, key: string, callback: () => T | Promise<T>): Promise<T> => {\n const config = getNamedLimiter(name);\n if (!config) {\n throw new Error(`Rate limiter [${name}] is not defined.`);\n }\n return rateLimiter.attempt(`${name}:${key}`, config.maxAttempts, callback, config.decaySeconds);\n },\n};\n\nexport default rateLimiter;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport crypto from \"crypto\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nimport CacheModel from \"./Models/Cache.js\";\n\n/** Optional watcher hook — registered by @lara-node/telescope at boot to avoid circular deps. */\ntype CacheEventType = \"get\" | \"set\" | \"del\" | \"has\" | \"clear\";\ninterface CacheWatchEvent {\n type: CacheEventType;\n key: string;\n hit?: boolean;\n value?: any;\n ttlSeconds?: number | null;\n}\nlet _cacheWatchHook: ((event: CacheWatchEvent) => void) | null = null;\nexport function setCacheWatchHook(hook: (event: CacheWatchEvent) => void): void {\n _cacheWatchHook = hook;\n}\nconst CacheWatcher = {\n record: (e: CacheWatchEvent) => {\n if (_cacheWatchHook)\n try {\n _cacheWatchHook(e);\n } catch {}\n },\n};\n\n// Re-export rate limiter\nexport {\n RateLimiter,\n RateLimiterFacade,\n RateLimitExceededException,\n defineRateLimiter,\n getNamedLimiter,\n} from \"./RateLimiter.js\";\nexport type { RateLimiterConfig, RateLimitInfo } from \"./RateLimiter.js\";\n\nexport interface CacheDriver {\n init(): Promise<void>;\n get(key: string): Promise<any | null>;\n set(key: string, value: any, ttlSeconds?: number | null): Promise<void>;\n del(key: string): Promise<boolean>;\n has(key: string): Promise<boolean>;\n clear(): Promise<void>;\n keys(): Promise<string[]>; // list raw (unprefixed) keys\n}\n\n// Encryption helpers (Laravel-like behavior)\nconst APP_KEY = process.env.APP_KEY || \"\";\nconst CIPHER = \"aes-256-cbc\";\nlet ENCRYPTION_ENABLED = false;\nlet ENCRYPTION_KEY: Buffer | null = null;\n\nfunction deriveKeyFromAppKey(appKey: string): Buffer {\n if (!appKey) return Buffer.alloc(0);\n if (appKey.startsWith(\"base64:\")) {\n const b = appKey.slice(7);\n return Buffer.from(b, \"base64\");\n }\n const buf = Buffer.from(appKey, \"utf8\");\n if (buf.length === 32) return buf;\n // derive 32 bytes via sha256\n return crypto.createHash(\"sha256\").update(buf).digest();\n}\n\nif (APP_KEY) {\n try {\n ENCRYPTION_KEY = deriveKeyFromAppKey(APP_KEY);\n if (ENCRYPTION_KEY.length !== 32) {\n console.warn(\n \"APP_KEY provided but did not yield 32 bytes; derived key length:\",\n ENCRYPTION_KEY.length,\n \"— disabling encryption\",\n );\n ENCRYPTION_ENABLED = false;\n } else {\n ENCRYPTION_ENABLED = true;\n }\n } catch (e) {\n console.warn(\"Failed to initialize cache encryption, proceeding without encryption:\", e);\n ENCRYPTION_ENABLED = false;\n }\n} else {\n // not fatal — allow running without encryption but warn\n console.warn(\n \"No APP_KEY set; cache encryption is disabled. Set APP_KEY in your .env to enable encryption of cached values.\",\n );\n}\n\nfunction hmacFor(ivB64: string, valueB64: string) {\n if (!ENCRYPTION_KEY) return \"\";\n return crypto\n .createHmac(\"sha256\", ENCRYPTION_KEY)\n .update(ivB64 + \"|\" + valueB64)\n .digest(\"hex\");\n}\n\nfunction encryptRaw(plain: string): string {\n if (!ENCRYPTION_ENABLED || !ENCRYPTION_KEY || ENCRYPTION_KEY.length !== 32) return plain;\n const iv = crypto.randomBytes(16);\n const cipher = crypto.createCipheriv(CIPHER, ENCRYPTION_KEY, iv);\n const encrypted = Buffer.concat([cipher.update(Buffer.from(plain, \"utf8\")), cipher.final()]);\n const ivB = iv.toString(\"base64\");\n const valB = encrypted.toString(\"base64\");\n const mac = hmacFor(ivB, valB);\n const payload = { iv: ivB, value: valB, mac };\n return JSON.stringify(payload);\n}\n\nfunction decryptRaw(payloadStr: string): string | null {\n if (!ENCRYPTION_ENABLED || !ENCRYPTION_KEY || ENCRYPTION_KEY.length !== 32) return payloadStr;\n let payload: any;\n try {\n payload = JSON.parse(payloadStr);\n } catch (e) {\n // not encrypted payload\n return null;\n }\n if (!payload || !payload.iv || !payload.value || !payload.mac) return null;\n const expected = hmacFor(payload.iv, payload.value);\n if (!crypto.timingSafeEqual(Buffer.from(expected, \"hex\"), Buffer.from(payload.mac, \"hex\"))) {\n throw new Error(\"Cache decryption failed: invalid MAC\");\n }\n const iv = Buffer.from(payload.iv, \"base64\");\n const enc = Buffer.from(payload.value, \"base64\");\n const decipher = crypto.createDecipheriv(CIPHER, ENCRYPTION_KEY as Buffer, iv);\n const decrypted = Buffer.concat([decipher.update(enc), decipher.final()]);\n return decrypted.toString(\"utf8\");\n}\n\nconst CACHE_PREFIX = process.env.CACHE_PREFIX\n ? String(process.env.CACHE_PREFIX)\n : process.env.APP_NAME || \"app\";\nfunction prefixed(key: string) {\n if (!CACHE_PREFIX) return key;\n return `${CACHE_PREFIX}:${key}`;\n}\n\nfunction stripPrefix(fullKey: string): string {\n if (!CACHE_PREFIX) return fullKey;\n return fullKey.startsWith(CACHE_PREFIX + \":\") ? fullKey.slice(CACHE_PREFIX.length + 1) : fullKey;\n}\nexport function generateCacheKey(\n ...parts: Array<string | number | boolean | Date | null | undefined>\n): string {\n const cleaned = parts\n .filter((p) => p !== undefined && p !== null)\n .map((p) => (p instanceof Date ? p.toISOString() : String(p).trim().replace(/\\s+/g, \"_\")));\n return cleaned.join(\":\"); // unprefixed base key\n}\n\nclass FileCache implements CacheDriver {\n private dir: string;\n private initialized = false;\n\n constructor(baseDir?: string) {\n this.dir = baseDir || path.resolve(__dirname, \"../../tmp/cache\");\n }\n\n async init() {\n if (this.initialized) return;\n await fs.promises.mkdir(this.dir, { recursive: true });\n this.initialized = true;\n }\n\n private filePath(key: string) {\n // sanitize key to file-friendly name\n const safe = encodeURIComponent(prefixed(key));\n return path.join(this.dir, `${safe}.json`);\n }\n\n async get(key: string) {\n await this.init();\n const p = this.filePath(key);\n try {\n const raw = await fs.promises.readFile(p, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed.expiresAt && Date.now() > parsed.expiresAt) {\n try {\n await fs.promises.unlink(p);\n } catch (e) {}\n return null;\n }\n const stored = parsed.value;\n // attempt decrypt\n if (typeof stored === \"string\") {\n const dec = decryptRaw(stored);\n if (dec !== null) {\n try {\n return JSON.parse(dec);\n } catch (e) {\n return dec;\n }\n }\n // not encrypted or decryption not enabled: return parsed raw\n return stored;\n }\n return parsed.value;\n } catch (e) {\n return null;\n }\n }\n\n async set(key: string, value: any, ttlSeconds?: number | null) {\n await this.init();\n const p = this.filePath(key);\n const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1000 : null;\n const toStore = typeof value === \"string\" ? value : JSON.stringify(value);\n const payloadVal = encryptRaw(toStore);\n const payload = { value: payloadVal, expiresAt };\n await fs.promises.writeFile(p, JSON.stringify(payload), \"utf8\");\n }\n\n async del(key: string) {\n await this.init();\n const p = this.filePath(key);\n try {\n await fs.promises.unlink(p);\n return true;\n } catch (e) {\n return false;\n }\n }\n\n async has(key: string) {\n const v = await this.get(key);\n return v !== null && v !== undefined;\n }\n\n async clear() {\n await this.init();\n const files = await fs.promises.readdir(this.dir);\n await Promise.all(files.map((f) => fs.promises.unlink(path.join(this.dir, f)).catch(() => {})));\n }\n\n async keys() {\n await this.init();\n const files = await fs.promises.readdir(this.dir);\n return files\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => decodeURIComponent(f.replace(/\\.json$/, \"\")))\n .map(stripPrefix);\n }\n}\n\nclass DBCache implements CacheDriver {\n private initialized = false;\n\n async init() {\n if (this.initialized) return;\n // Assume migrations create table; avoid raw DDL here.\n this.initialized = true;\n }\n\n private now() {\n return Date.now();\n }\n\n async get(key: string) {\n await this.init();\n const record: any = await (CacheModel as any).where(\"k\", prefixed(key)).first();\n if (!record) return null;\n const expiresAt = record.getAttribute ? record.getAttribute(\"expires_at\") : record.expires_at;\n if (expiresAt && this.now() > Number(expiresAt)) {\n await this.del(key);\n return null;\n }\n let rawVal = record.getAttribute ? record.getAttribute(\"v\") : record.v;\n if (typeof rawVal === \"string\") {\n const dec = decryptRaw(rawVal);\n if (dec !== null) {\n try {\n return JSON.parse(dec);\n } catch {\n return dec;\n }\n }\n try {\n return JSON.parse(rawVal);\n } catch {\n return rawVal;\n }\n }\n return rawVal;\n }\n\n async set(key: string, value: any, ttlSeconds?: number | null) {\n await this.init();\n const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1000 : null;\n const raw = typeof value === \"string\" ? value : JSON.stringify(value);\n const stored = encryptRaw(raw);\n let record: any = await (CacheModel as any).where(\"k\", prefixed(key)).first();\n if (record) {\n // update existing\n if (record.setAttribute) {\n record.setAttribute(\"v\", stored);\n record.setAttribute(\"expires_at\", expiresAt);\n await record.save();\n } else {\n record.v = stored;\n record.expires_at = expiresAt;\n await record.save();\n }\n } else {\n // create new\n await (CacheModel as any).create({ k: prefixed(key), v: stored, expires_at: expiresAt });\n }\n }\n\n async del(key: string) {\n await this.init();\n const record: any = await (CacheModel as any).where(\"k\", prefixed(key)).first();\n if (!record) return false;\n await record.delete(true); // force physical delete (no soft deletes configured)\n return true;\n }\n\n async has(key: string) {\n const v = await this.get(key);\n return v !== null && v !== undefined;\n }\n\n async clear() {\n await this.init();\n const all: any[] = await (CacheModel as any).query().get();\n for (const rec of all) {\n try {\n await rec.delete(true);\n } catch {}\n }\n }\n\n async keys() {\n await this.init();\n const rows: any[] = await (CacheModel as any).query().get();\n return rows.map((r) => (r.getAttribute ? r.getAttribute(\"k\") : r.k)).map(stripPrefix);\n }\n}\n\nclass RedisCache implements CacheDriver {\n private client: any = null;\n private initialized = false;\n\n async init() {\n if (this.initialized) return;\n let createClient: any;\n try {\n const redis = await import(\"redis\");\n createClient = redis.createClient;\n } catch (e) {\n throw new Error(\n 'Redis driver selected (CACHE_DRIVER=redis) but \"redis\" package is not installed. Install it with `npm install redis`.',\n );\n }\n\n const redisUrl = process.env.REDIS_URL || undefined;\n const host = process.env.REDIS_HOST || undefined;\n const port = process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT, 10) : undefined;\n const password = process.env.REDIS_PASSWORD || undefined;\n\n const opts: any = {};\n if (redisUrl) opts.url = redisUrl;\n if (host) {\n opts.socket = { host };\n if (port) opts.socket.port = port;\n }\n if (password) opts.password = password;\n\n this.client = createClient(opts);\n if (typeof this.client.connect === \"function\") {\n await this.client.connect();\n }\n this.initialized = true;\n }\n\n async get(key: string) {\n await this.init();\n const res = await this.client.get(prefixed(key));\n if (res === null) return null;\n const dec = decryptRaw(res);\n if (dec !== null) {\n try {\n return JSON.parse(dec);\n } catch (e) {\n return dec;\n }\n }\n try {\n return JSON.parse(res);\n } catch (e) {\n return res;\n }\n }\n\n async set(key: string, value: any, ttlSeconds?: number | null) {\n await this.init();\n const v = typeof value === \"string\" ? value : JSON.stringify(value);\n const stored = encryptRaw(v);\n if (ttlSeconds && ttlSeconds > 0) {\n await this.client.set(prefixed(key), stored, { EX: ttlSeconds });\n } else {\n await this.client.set(prefixed(key), stored);\n }\n }\n\n async del(key: string) {\n await this.init();\n const n = await this.client.del(prefixed(key));\n return n > 0;\n }\n\n async has(key: string) {\n await this.init();\n const exists = await this.client.exists(prefixed(key));\n return exists === 1 || exists === true || exists > 0;\n }\n\n async clear() {\n await this.init();\n // Use SCAN + DEL scoped to our prefix instead of FLUSHDB\n // FLUSHDB is dangerous in shared Redis environments as it wipes ALL keys\n const pattern = CACHE_PREFIX ? `${CACHE_PREFIX}:*` : \"*\";\n let cursor = \"0\";\n do {\n const res = await this.client.scan(cursor, { MATCH: pattern, COUNT: 200 });\n cursor = res.cursor || res[0];\n const keys = res.keys || res[1];\n if (keys.length > 0) {\n await this.client.del(keys);\n }\n } while (cursor !== \"0\" && String(cursor) !== \"0\");\n }\n\n async keys() {\n await this.init();\n // Use SCAN for safety (avoid KEYS on large datasets)\n const pattern = CACHE_PREFIX ? `${CACHE_PREFIX}:*` : \"*\";\n const out: string[] = [];\n let cursor = \"0\";\n do {\n const res = await this.client.scan(cursor, { MATCH: pattern, COUNT: 100 });\n cursor = res.cursor || res[0];\n const keys = res.keys || res[1];\n for (const k of keys) out.push(stripPrefix(k));\n } while (cursor !== \"0\");\n return out;\n }\n}\n\nclass CacheManager implements CacheDriver {\n private driver: CacheDriver | null = null;\n private initializing: Promise<void> | null = null;\n\n private createDriver(): CacheDriver {\n const driver = (process.env.CACHE_DRIVER || \"file\").toLowerCase();\n if (driver === \"redis\") return new RedisCache();\n if (driver === \"database\" || driver === \"db\") return new DBCache();\n return new FileCache();\n }\n\n private async ensureInit() {\n if (this.driver) return;\n if (!this.initializing) {\n this.driver = this.createDriver();\n this.initializing = (async () => {\n await this.driver!.init();\n this.initializing = null;\n })();\n }\n await this.initializing;\n }\n\n async init() {\n await this.ensureInit();\n }\n\n async get(key: string) {\n await this.ensureInit();\n return this.driver!.get(key);\n }\n\n async set(key: string, value: any, ttlSeconds?: number | null) {\n await this.ensureInit();\n return this.driver!.set(key, value, ttlSeconds);\n }\n\n async del(key: string) {\n await this.ensureInit();\n return this.driver!.del(key);\n }\n\n async has(key: string) {\n await this.ensureInit();\n return this.driver!.has(key);\n }\n\n async clear() {\n await this.ensureInit();\n return this.driver!.clear();\n }\n\n async keys() {\n await this.ensureInit();\n return this.driver!.keys();\n }\n}\n\n// Export a singleton instance\nconst manager = new CacheManager();\nexport default manager;\n\n// Cache facade for convenient access\nexport const Cache = {\n get: async (k: string) => {\n const value = await manager.get(k);\n CacheWatcher.record({ type: \"get\", key: k, hit: value !== null, value });\n return value;\n },\n set: async (k: string, v: any, ttlSeconds?: number | null) => {\n await manager.set(k, v, ttlSeconds);\n CacheWatcher.record({ type: \"set\", key: k, value: v, ttlSeconds });\n },\n del: async (k: string) => {\n const result = await manager.del(k);\n CacheWatcher.record({ type: \"del\", key: k });\n return result;\n },\n has: async (k: string) => {\n const result = await manager.has(k);\n CacheWatcher.record({ type: \"has\", key: k, hit: result });\n return result;\n },\n clear: async () => {\n await manager.clear();\n CacheWatcher.record({ type: \"clear\", key: \"\" });\n },\n keys: () => manager.keys(),\n forget: async (k: string) => {\n const result = await manager.del(k);\n CacheWatcher.record({ type: \"del\", key: k });\n return result;\n },\n flush: async () => {\n await manager.clear();\n CacheWatcher.record({ type: \"clear\", key: \"\" });\n },\n remember: async <T>(\n key: string,\n ttlSeconds: number | null,\n callback: () => Promise<T>,\n ): Promise<T> => {\n const cached = await manager.get(key);\n if (cached !== null) {\n CacheWatcher.record({ type: \"get\", key, hit: true, value: cached, ttlSeconds });\n return cached as T;\n }\n CacheWatcher.record({ type: \"get\", key, hit: false, ttlSeconds });\n const value = await callback();\n await manager.set(key, value, ttlSeconds);\n CacheWatcher.record({ type: \"set\", key, value, ttlSeconds });\n return value;\n },\n};\n\n// Get cache driver name\nexport const getCacheDriverName = (): string => {\n return (process.env.CACHE_DRIVER || \"file\").toLowerCase();\n};\n\n// Get cache driver instance\nexport const getCacheDriver = () => manager;\n\n// Convenience named exports\nexport const initCache = async () => manager.init();\nexport const cacheGet = async (k: string) => manager.get(k);\nexport const cacheSet = async (k: string, v: any, ttlSeconds?: number | null) =>\n manager.set(k, v, ttlSeconds);\nexport const cacheDel = async (k: string) => manager.del(k);\nexport const cacheHas = async (k: string) => manager.has(k);\nexport const cacheClear = async () => manager.clear();\nexport const cacheKeys = async () => manager.keys();\n\n// Delete all cache keys that start with the provided (unprefixed) prefix.\n// Returns number of keys deleted.\nexport const cacheDelPrefix = async (prefix: string): Promise<number> => {\n // manager.keys() returns unprefixed keys (stripPrefix applied in drivers)\n const keys = await manager.keys();\n const matches = keys.filter((k) => k.startsWith(prefix));\n await Promise.all(matches.map((k) => manager.del(k).catch(() => {})));\n return matches.length;\n};\n","import { ServiceProvider } from \"@lara-node/core\";\nimport { initCache, getCacheDriver as getCache, getCacheDriverName } from \"./CacheManager.js\";\n\nexport class CacheServiceProvider extends ServiceProvider {\n register(): void {\n this.container.singleton(\"cache\", () => getCache());\n }\n\n async boot(): Promise<void> {\n const skip = (process.env.SKIP_CACHE ?? \"\").toLowerCase();\n if (skip === \"1\" || skip === \"true\") {\n console.warn(\"[Cache] SKIP_CACHE set — skipping cache initialization\");\n return;\n }\n try {\n await initCache();\n console.log(`[Cache] Initialized (driver=${getCacheDriverName()})`);\n } catch (err: any) {\n console.error(\"[Cache] Initialization failed:\", err.message);\n }\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@lara-node/cache",
3
+ "version": "0.1.0",
4
+ "description": "Lara-Node cache manager — file, database, and Redis drivers",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "tsdown",
25
+ "dev": "tsdown --watch",
26
+ "clean": "rimraf dist",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run"
29
+ },
30
+ "dependencies": {
31
+ "@lara-node/core": "workspace:*",
32
+ "@lara-node/db": "workspace:*",
33
+ "redis": "^5.10.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^24.12.2",
37
+ "rimraf": "^6.0.1",
38
+ "tsdown": "^0.12.9",
39
+ "typescript": "^5.9.3",
40
+ "vitest": "^3.2.3"
41
+ }
42
+ }