sec-edgar-toolkit
Version:
Open source toolkit to facilitate working with the SEC EDGAR database
295 lines • 8.15 kB
JavaScript
"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