UNPKG

sec-edgar-toolkit

Version:

Open source toolkit to facilitate working with the SEC EDGAR database

295 lines 8.15 kB
"use strict"; /** * Comprehensive caching system for SEC EDGAR data */ Object.defineProperty(exports, "__esModule", { value: true }); exports.requestCache = exports.globalCache = exports.RequestCache = exports.MultiLevelCache = exports.Cache = void 0; exports.cacheable = cacheable; const crypto_1 = require("crypto"); class Cache { constructor(options = {}) { this.cache = new Map(); this.accessOrder = []; this.hits = 0; this.misses = 0; this.options = { ttl: options.ttl || 3600000, // 1 hour default maxSize: options.maxSize || 1000, persistent: options.persistent || false, }; } /** * Get an item from cache */ get(key) { const entry = this.cache.get(key); if (!entry) { this.misses++; return null; } // Check if expired if (entry.expiry && Date.now() > entry.expiry) { this.cache.delete(key); this.removeFromAccessOrder(key); this.misses++; return null; } // Update access order for LRU this.updateAccessOrder(key); this.hits++; return entry.data; } /** * Set an item in cache */ set(key, data, ttl) { const expiry = ttl || this.options.ttl; // Check size limit if (this.cache.size >= this.options.maxSize && !this.cache.has(key)) { this.evictLRU(); } const entry = { data, timestamp: Date.now(), expiry: expiry > 0 ? Date.now() + expiry : undefined, }; this.cache.set(key, entry); this.updateAccessOrder(key); } /** * Delete an item from cache */ delete(key) { this.removeFromAccessOrder(key); return this.cache.delete(key); } /** * Clear all cache entries */ clear() { this.cache.clear(); this.accessOrder = []; } /** * Check if key exists in cache */ has(key) { const entry = this.cache.get(key); if (!entry) return false; // Check expiry if (entry.expiry && Date.now() > entry.expiry) { this.delete(key); return false; } return true; } /** * Get cache size */ get size() { return this.cache.size; } /** * Get all keys */ keys() { return Array.from(this.cache.keys()); } /** * Get cache statistics */ getStats() { return { size: this.cache.size, hits: this.hits, misses: this.misses, hitRate: this.hits + this.misses > 0 ? this.hits / (this.hits + this.misses) : 0, }; } /** * Invalidate entries matching a pattern */ invalidatePattern(pattern) { let count = 0; for (const key of this.cache.keys()) { if (pattern.test(key)) { this.delete(key); count++; } } return count; } /** * Clean up expired entries */ cleanup() { let count = 0; const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (entry.expiry && now > entry.expiry) { this.delete(key); count++; } } return count; } /** * Generate cache key from multiple values */ static generateKey(...values) { const hash = (0, crypto_1.createHash)('md5'); hash.update(JSON.stringify(values)); return hash.digest('hex'); } /** * Update access order for LRU eviction */ updateAccessOrder(key) { this.removeFromAccessOrder(key); this.accessOrder.push(key); } /** * Remove key from access order */ removeFromAccessOrder(key) { const index = this.accessOrder.indexOf(key); if (index > -1) { this.accessOrder.splice(index, 1); } } /** * Evict least recently used item */ evictLRU() { if (this.accessOrder.length > 0) { const lruKey = this.accessOrder[0]; this.delete(lruKey); } } } exports.Cache = Cache; /** * Decorator for caching method results */ function cacheable(options = {}) { const cache = new Cache(options); return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = async function (...args) { const cacheKey = Cache.generateKey(propertyKey, ...args); // Check cache first const cached = cache.get(cacheKey); if (cached !== null) { return cached; } // Call original method const result = await originalMethod.apply(this, args); // Cache result cache.set(cacheKey, result); return result; }; return descriptor; }; } /** * Multi-level cache supporting memory and persistent storage */ class MultiLevelCache { constructor(options = {}) { this.memoryCache = new Cache(options); if (options.persistent) { // Initialize persistent cache (implementation would depend on environment) // For Node.js: use file system // For browser: use IndexedDB or localStorage } } async get(key) { // Check memory cache first let data = this.memoryCache.get(key); if (data !== null) { return data; } // Check persistent cache if (this.persistentCache) { data = await this.persistentCache.get(key); if (data !== null) { // Promote to memory cache this.memoryCache.set(key, data); return data; } } return null; } async set(key, data, ttl) { // Set in memory cache this.memoryCache.set(key, data, ttl); // Set in persistent cache if (this.persistentCache) { await this.persistentCache.set(key, data, ttl); } } async delete(key) { const memoryDeleted = this.memoryCache.delete(key); let persistentDeleted = false; if (this.persistentCache) { persistentDeleted = await this.persistentCache.delete(key); } return memoryDeleted || persistentDeleted; } async clear() { this.memoryCache.clear(); if (this.persistentCache) { await this.persistentCache.clear(); } } } exports.MultiLevelCache = MultiLevelCache; /** * Request cache specifically for HTTP responses */ class RequestCache { constructor(options = {}) { this.cache = new MultiLevelCache(options); } /** * Generate cache key for HTTP request */ generateRequestKey(url, options) { return Cache.generateKey(url, options); } /** * Get cached response */ async get(url, options) { const key = this.generateRequestKey(url, options); return await this.cache.get(key); } /** * Cache response */ async set(url, response, options, ttl) { const key = this.generateRequestKey(url, options); await this.cache.set(key, response, ttl); } /** * Delete cached response */ async delete(url, options) { const key = this.generateRequestKey(url, options); return await this.cache.delete(key); } /** * Invalidate all cache entries for a base URL */ async invalidateUrl(_baseUrl) { // This would need to track keys by URL pattern // For now, simplified implementation await this.cache.clear(); } } exports.RequestCache = RequestCache; // Export singleton instances for common use cases exports.globalCache = new Cache({ ttl: 3600000, maxSize: 1000 }); exports.requestCache = new RequestCache({ ttl: 300000, maxSize: 500 }); //# sourceMappingURL=cache.js.map