react-native-avo-inspector
Version:
[](https://badge.fury.io/js/react-native-avo-inspector)
182 lines (181 loc) • 6.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventSpecCache = void 0;
/**
* EventSpecCache implements a dual-condition cache with LRU eviction.
*
* Cache Policy:
* - TTL: Entries expire after 60 seconds
* - Per-entry eviction: Entries retrieved 50+ times evicted immediately on next get
* - Global sweep: Runs every 50 cache operations (get/set)
* - Capacity: LRU eviction when size > 50 entries
* - Null spec responses: cached (to avoid re-fetching)
* - branchId flush: cache cleared when branchId changes (handled externally)
*
* Cache Key Format: ${apiKey}:${streamId}:${eventName}
*/
var EventSpecCache = /** @class */ (function () {
function EventSpecCache(shouldLog) {
if (shouldLog === void 0) { shouldLog = false; }
/** Time-to-live in milliseconds (60 seconds) */
this.TTL_MS = 60 * 1000;
/** Maximum cache hit count before per-entry eviction (50 hits) */
this.MAX_EVENT_COUNT = 50;
/** Maximum number of cache entries */
this.MAX_ENTRIES = 50;
/** Global cache operation counter to track when to sweep */
this.globalEventCount = 0;
this.cache = new Map();
this.shouldLog = shouldLog;
}
/**
* Generates a cache key from the provided parameters.
*/
EventSpecCache.prototype.generateKey = function (apiKey, streamId, eventName) {
return "".concat(apiKey, ":").concat(streamId, ":").concat(eventName);
};
/**
* Retrieves an event spec response from the cache if it exists and is valid.
* Returns undefined (cache miss) if the entry is missing.
* Returns null if a null spec was cached (known absent).
* Returns the spec if valid.
*
* On cache hit, increments the hit count for this entry and the global counter.
*/
EventSpecCache.prototype.get = function (apiKey, streamId, eventName) {
var key = this.generateKey(apiKey, streamId, eventName);
var entry = this.cache.get(key);
if (!entry) {
return undefined;
}
// Check if entry has expired by TTL
if (this.isExpiredByTTL(entry)) {
this.cache.delete(key);
return undefined;
}
// Check per-entry eviction (50+ accesses)
if (entry.eventCount >= this.MAX_EVENT_COUNT) {
this.cache.delete(key);
return undefined;
}
if (this.shouldLog) {
console.log("[Avo Inspector] Cache hit for key: ".concat(key));
}
// Update lastAccessed for LRU eviction
entry.lastAccessed = Date.now();
// Increment hit count for this entry
entry.eventCount++;
this.globalEventCount++;
// Global sweep every 50 operations
if (this.globalEventCount >= this.MAX_EVENT_COUNT) {
this.sweep();
this.globalEventCount = 0;
}
return entry.spec;
};
/**
* Stores an event spec response in the cache.
* Null responses are cached to prevent re-fetching for known absent specs.
*/
EventSpecCache.prototype.set = function (apiKey, streamId, eventName, spec) {
var key = this.generateKey(apiKey, streamId, eventName);
// Enforce capacity limit with LRU eviction
if (!this.cache.has(key) && this.cache.size >= this.MAX_ENTRIES) {
this.evictLRU();
}
var now = Date.now();
var entry = {
spec: spec,
timestamp: now,
lastAccessed: now,
eventCount: 0
};
this.cache.set(key, entry);
this.globalEventCount++;
// Global sweep every 50 operations
if (this.globalEventCount >= this.MAX_EVENT_COUNT) {
this.sweep();
this.globalEventCount = 0;
}
};
/**
* Checks if a cache entry has expired by TTL (older than 60s).
*/
EventSpecCache.prototype.isExpiredByTTL = function (entry) {
var age = Date.now() - entry.timestamp;
return age > this.TTL_MS;
};
/**
* Sweeps the cache: removes all entries expired by TTL.
*/
EventSpecCache.prototype.sweep = function () {
var _this = this;
var keysToDelete = [];
this.cache.forEach(function (entry, key) {
if (_this.isExpiredByTTL(entry)) {
keysToDelete.push(key);
}
});
for (var _i = 0, keysToDelete_1 = keysToDelete; _i < keysToDelete_1.length; _i++) {
var key = keysToDelete_1[_i];
this.cache.delete(key);
}
};
/**
* Evicts the least recently used cache entry based on lastAccessed timestamp.
*/
EventSpecCache.prototype.evictLRU = function () {
if (this.cache.size === 0) {
return;
}
var lruKey = null;
var oldestAccessTime = Infinity;
this.cache.forEach(function (entry, key) {
if (entry.lastAccessed < oldestAccessTime) {
oldestAccessTime = entry.lastAccessed;
lruKey = key;
}
});
if (lruKey !== null) {
this.cache.delete(lruKey);
}
};
/**
* Clears all cached entries.
*/
EventSpecCache.prototype.clear = function () {
this.cache.clear();
this.globalEventCount = 0;
if (this.shouldLog) {
console.log("[Avo Inspector] Cache cleared");
}
};
/**
* Returns the current size of the cache.
*/
EventSpecCache.prototype.size = function () {
return this.cache.size;
};
/**
* Returns cache statistics for debugging.
*/
EventSpecCache.prototype.getStats = function () {
var entries = [];
var now = Date.now();
this.cache.forEach(function (entry, key) {
entries.push({
key: key,
age: now - entry.timestamp,
lastAccessedAgo: now - entry.lastAccessed,
eventCount: entry.eventCount
});
});
return {
size: this.cache.size,
globalEventCount: this.globalEventCount,
entries: entries
};
};
return EventSpecCache;
}());
exports.EventSpecCache = EventSpecCache;