Progressive breadcrumb resolution with caching, fix double headers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
142 lines
2.9 KiB
JavaScript
142 lines
2.9 KiB
JavaScript
/**
|
|
* JQHTML SSR Bundle Cache
|
|
*
|
|
* Caches parsed bundle code to avoid re-parsing on every request.
|
|
* Uses LRU (Least Recently Used) eviction strategy.
|
|
*/
|
|
|
|
/**
|
|
* LRU Cache for bundle sets
|
|
*/
|
|
class BundleCache {
|
|
constructor(maxSize = 10) {
|
|
this.maxSize = maxSize;
|
|
this.cache = new Map(); // bundleSetId -> { code, lastUsed }
|
|
}
|
|
|
|
/**
|
|
* Generate a cache key from bundle array
|
|
* @param {Array<{id: string, content: string}>} bundles
|
|
* @returns {string} Cache key
|
|
*/
|
|
static generateKey(bundles) {
|
|
// Key is concatenation of all bundle IDs in order
|
|
return bundles.map(b => b.id).join('|');
|
|
}
|
|
|
|
/**
|
|
* Get cached bundle code if available
|
|
* @param {string} key - Cache key from generateKey()
|
|
* @returns {string|null} Concatenated bundle code or null if not cached
|
|
*/
|
|
get(key) {
|
|
const entry = this.cache.get(key);
|
|
if (entry) {
|
|
entry.lastUsed = Date.now();
|
|
return entry.code;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Store bundle code in cache
|
|
* @param {string} key - Cache key
|
|
* @param {string} code - Concatenated bundle code
|
|
*/
|
|
set(key, code) {
|
|
// Evict if at capacity
|
|
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
this._evictLRU();
|
|
}
|
|
|
|
this.cache.set(key, {
|
|
code,
|
|
lastUsed: Date.now()
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check if a bundle set is cached
|
|
* @param {string} key - Cache key
|
|
* @returns {boolean}
|
|
*/
|
|
has(key) {
|
|
return this.cache.has(key);
|
|
}
|
|
|
|
/**
|
|
* Remove a specific bundle set from cache
|
|
* @param {string} key - Cache key
|
|
* @returns {boolean} True if entry was removed
|
|
*/
|
|
delete(key) {
|
|
return this.cache.delete(key);
|
|
}
|
|
|
|
/**
|
|
* Clear all cached bundles
|
|
*/
|
|
clear() {
|
|
this.cache.clear();
|
|
}
|
|
|
|
/**
|
|
* Get cache statistics
|
|
* @returns {{ size: number, maxSize: number, keys: string[] }}
|
|
*/
|
|
stats() {
|
|
return {
|
|
size: this.cache.size,
|
|
maxSize: this.maxSize,
|
|
keys: Array.from(this.cache.keys())
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Evict the least recently used entry
|
|
* @private
|
|
*/
|
|
_evictLRU() {
|
|
let oldestKey = null;
|
|
let oldestTime = Infinity;
|
|
|
|
for (const [key, entry] of this.cache) {
|
|
if (entry.lastUsed < oldestTime) {
|
|
oldestTime = entry.lastUsed;
|
|
oldestKey = key;
|
|
}
|
|
}
|
|
|
|
if (oldestKey) {
|
|
this.cache.delete(oldestKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prepare bundle code for execution
|
|
* Concatenates bundles and removes problematic patterns
|
|
* @param {Array<{id: string, content: string}>} bundles
|
|
* @returns {string} Prepared code
|
|
*/
|
|
function prepareBundleCode(bundles) {
|
|
const codeChunks = [];
|
|
|
|
for (const bundle of bundles) {
|
|
// Remove sourcemap comments
|
|
let code = bundle.content.replace(/\/\/# sourceMappingURL=.*/g, '');
|
|
|
|
// Add bundle marker comment for debugging
|
|
code = `\n/* === Bundle: ${bundle.id} === */\n${code}`;
|
|
|
|
codeChunks.push(code);
|
|
}
|
|
|
|
return codeChunks.join('\n');
|
|
}
|
|
|
|
module.exports = {
|
|
BundleCache,
|
|
prepareBundleCode
|
|
};
|