bc-symbols-mcp
Version:
MCP server for analyzing Business Central .app files and BC object structures
212 lines • 5.9 kB
JavaScript
export class MemoryCache {
cache = {};
maxAge;
constructor(maxAgeMinutes = 60) {
this.maxAge = maxAgeMinutes * 60 * 1000; // Convert to milliseconds
}
/**
* Store an app in the cache
*/
set(filePath, app) {
this.cache[filePath] = {
data: app,
timestamp: Date.now(),
fileHash: app.fileHash
};
}
/**
* Retrieve an app from the cache
* Returns null if not found, expired, or file hash doesn't match
*/
get(filePath, currentFileHash) {
const entry = this.cache[filePath];
if (!entry) {
return null;
}
// Check if entry has expired
if (Date.now() - entry.timestamp > this.maxAge) {
delete this.cache[filePath];
return null;
}
// Check if file hash matches (file hasn't been modified)
if (entry.fileHash !== currentFileHash) {
delete this.cache[filePath];
return null;
}
return entry.data;
}
/**
* Check if an app is cached and valid
*/
has(filePath, currentFileHash) {
return this.get(filePath, currentFileHash) !== null;
}
/**
* Remove an app from the cache
*/
delete(filePath) {
if (this.cache[filePath]) {
delete this.cache[filePath];
return true;
}
return false;
}
/**
* Clear all cached apps
*/
clear() {
this.cache = {};
}
/**
* Get all cached file paths
*/
getCachedPaths() {
return Object.keys(this.cache);
}
/**
* Get all cached apps that are still valid
*/
getValidCachedApps() {
const now = Date.now();
const validApps = [];
for (const [filePath, entry] of Object.entries(this.cache)) {
// Check if entry has expired
if (now - entry.timestamp > this.maxAge) {
delete this.cache[filePath];
continue;
}
validApps.push(entry.data);
}
return validApps;
}
/**
* Clean up expired entries
*/
cleanup() {
const now = Date.now();
let removedCount = 0;
for (const [filePath, entry] of Object.entries(this.cache)) {
if (now - entry.timestamp > this.maxAge) {
delete this.cache[filePath];
removedCount++;
}
}
return removedCount;
}
/**
* Get cache statistics
*/
getStats() {
const now = Date.now();
let validEntries = 0;
let expiredEntries = 0;
let totalMemoryUsage = 0;
for (const entry of Object.values(this.cache)) {
if (now - entry.timestamp > this.maxAge) {
expiredEntries++;
}
else {
validEntries++;
}
// Rough estimate of memory usage
totalMemoryUsage += JSON.stringify(entry.data).length;
}
return {
totalEntries: Object.keys(this.cache).length,
validEntries,
expiredEntries,
totalMemoryUsage
};
}
/**
* Find cached apps by criteria
*/
findApps(predicate) {
const validApps = this.getValidCachedApps();
return validApps.filter(predicate);
}
/**
* Find a cached app by app ID
*/
findAppById(appId) {
const validApps = this.getValidCachedApps();
return validApps.find(app => app.id === appId) || null;
}
/**
* Find cached apps by publisher
*/
findAppsByPublisher(publisher) {
return this.findApps(app => app.publisher === publisher);
}
/**
* Find cached apps that depend on a specific app
*/
findAppsDependingOn(targetAppId) {
return this.findApps(app => app.dependencies.some(dep => dep.id === targetAppId));
}
/**
* Get dependency tree for an app
*/
getDependencyTree(appId) {
const app = this.findAppById(appId);
if (!app) {
return [];
}
const dependencies = new Set();
const visited = new Set();
const collectDependencies = (currentAppId) => {
if (visited.has(currentAppId)) {
return; // Avoid circular dependencies
}
visited.add(currentAppId);
const currentApp = this.findAppById(currentAppId);
if (currentApp) {
for (const dep of currentApp.dependencies) {
dependencies.add(dep.id);
collectDependencies(dep.id);
}
}
};
collectDependencies(appId);
return Array.from(dependencies);
}
/**
* Get reverse dependency tree (apps that depend on this app)
*/
getReverseDependencyTree(appId) {
const dependents = new Set();
const visited = new Set();
const collectDependents = (targetAppId) => {
if (visited.has(targetAppId)) {
return;
}
visited.add(targetAppId);
const dependentApps = this.findAppsDependingOn(targetAppId);
for (const app of dependentApps) {
dependents.add(app.id);
collectDependents(app.id);
}
};
collectDependents(appId);
return Array.from(dependents);
}
/**
* Set cache expiration time
*/
setMaxAge(maxAgeMinutes) {
this.maxAge = maxAgeMinutes * 60 * 1000;
}
/**
* Check if cache is empty
*/
isEmpty() {
return Object.keys(this.cache).length === 0;
}
/**
* Get cache size in entries
*/
size() {
return Object.keys(this.cache).length;
}
}
//# sourceMappingURL=memory-cache.js.map