cacheDebugger: {
    getCacheStatus(): {
        inMemoryCache: number;
        localStorage: { mangaCache: number; searchCache: number };
    };
    checkMangaInCache(
        title: string,
    ): {
        found: boolean;
        cacheKey: string;
        entry?: { mangaCount: number; timestamp: number; age: string };
    };
    forceSyncCaches(): void;
    resetAllCaches(): void;
    clearCacheEntryForTitle(title: string): boolean;
    clearCacheForTitles(titles: string[]): number;
    clearAllCaches(): {
        inMemoryCache: number;
        localStorage: { mangaCache: number; searchCache: number };
    };
    printCacheKeysFor(title: string): string;
    dumpCache(): {
        [key: string]: { manga: AniListManga[]; timestamp: number };
    };
} = ...

Debug and troubleshoot the cache status. Exposes functions to check and diagnose cache issues.

Type declaration

  • getCacheStatus:function
    • Get a summary of the current cache status

      Returns {
          inMemoryCache: number;
          localStorage: { mangaCache: number; searchCache: number };
      }

  • checkMangaInCache:function
    • Check if a specific manga title is in cache

      Parameters

      • title: string

      Returns {
          found: boolean;
          cacheKey: string;
          entry?: { mangaCount: number; timestamp: number; age: string };
      }

  • forceSyncCaches:function
  • resetAllCaches:function
  • clearCacheEntryForTitle:function
    • Clear cache entry for a specific manga title

      Parameters

      • title: string

        The manga title to clear from cache

      Returns boolean

      boolean True if an entry was cleared, false if no entry was found

  • clearCacheForTitles:function
    • Clear cache entries for multiple manga titles at once

      Parameters

      • titles: string[]

        Array of manga titles to clear from cache

      Returns number

      number Number of cache entries cleared

  • clearAllCaches:function
    • Returns {
          inMemoryCache: number;
          localStorage: { mangaCache: number; searchCache: number };
      }

  • printCacheKeysFor:function
  • dumpCache:function
export const cacheDebugger = {
/**
* Get a summary of the current cache status
*/
getCacheStatus(): {
inMemoryCache: number;
localStorage: {
mangaCache: number;
searchCache: number;
};
} {
// Check in-memory cache
const inMemoryCount = Object.keys(mangaCache).length;

// Check localStorage
let storedMangaCount = 0;
let storedSearchCount = 0;

if (typeof window !== "undefined") {
try {
const mangaCacheData = localStorage.getItem("anilist_manga_cache");
if (mangaCacheData) {
const parsed = JSON.parse(mangaCacheData);
storedMangaCount = Object.keys(parsed).length;
}

const searchCacheData = localStorage.getItem("anilist_search_cache");
if (searchCacheData) {
const parsed = JSON.parse(searchCacheData);
storedSearchCount = Object.keys(parsed).length;
}
} catch (e) {
console.error("Error checking localStorage cache:", e);
}
}

return {
inMemoryCache: inMemoryCount,
localStorage: {
mangaCache: storedMangaCount,
searchCache: storedSearchCount,
},
};
},

/**
* Check if a specific manga title is in cache
*/
checkMangaInCache(title: string): {
found: boolean;
cacheKey: string;
entry?: {
mangaCount: number;
timestamp: number;
age: string;
};
} {
const cacheKey = generateCacheKey(title);
const entry = mangaCache[cacheKey];

if (!entry) {
return { found: false, cacheKey };
}

// Calculate age
const ageMs = Date.now() - entry.timestamp;
const ageMinutes = Math.floor(ageMs / 60000);

let age: string;
if (ageMinutes < 60) {
age = `${ageMinutes} minute(s)`;
} else if (ageMinutes < 1440) {
age = `${Math.floor(ageMinutes / 60)} hour(s)`;
} else {
age = `${Math.floor(ageMinutes / 1440)} day(s)`;
}

return {
found: true,
cacheKey,
entry: {
mangaCount: entry.manga.length,
timestamp: entry.timestamp,
age,
},
};
},

/**
* Force a sync of the caches
*/
forceSyncCaches(): void {
syncWithClientCache();
console.log("Cache sync forced, current status:");
console.log(this.getCacheStatus());
},

/**
* Reset all caches (both in-memory and localStorage)
*/
resetAllCaches(): void {
// Clear in-memory cache
clearMangaCache();

// Clear localStorage caches
if (typeof window !== "undefined") {
try {
localStorage.removeItem("anilist_manga_cache");
localStorage.removeItem("anilist_search_cache");
console.log("All AniList caches have been cleared");
} catch (e) {
console.error("Error clearing localStorage caches:", e);
}
}
},

/**
* Clear cache entry for a specific manga title
* @param title The manga title to clear from cache
* @returns boolean True if an entry was cleared, false if no entry was found
*/
clearCacheEntryForTitle(title: string): boolean {
// Generate cache key for the title
const mainKey = generateCacheKey(title);
let cleared = false;

// Check if we have this entry in the cache
if (mangaCache[mainKey]) {
delete mangaCache[mainKey];
cleared = true;
}

// Try alternate forms of the title (English title/native title)
// This should only match EXACT English/Native titles, not partial matches
const titleLower = title.toLowerCase().trim();

// Track entries to remove (to avoid modifying while iterating)
const keysToRemove: string[] = [];

// Look for entries that may be this exact manga but stored under a different title variant
Object.keys(mangaCache).forEach((key) => {
if (key === mainKey) return; // Skip the main key we already handled

// Check if this cache entry is for this specific manga (by exact title match)
const entries = mangaCache[key].manga;

for (const manga of entries) {
if (!manga.title) continue;

// Only compare exact matches for English/romaji titles
const romajiTitle = manga.title.romaji
? manga.title.romaji.toLowerCase().trim()
: "";
const englishTitle = manga.title.english
? manga.title.english.toLowerCase().trim()
: "";

// Only delete if it's an exact title match, not partial matches
if (
(romajiTitle && romajiTitle === titleLower) ||
(englishTitle && englishTitle === titleLower)
) {
keysToRemove.push(key);
break; // No need to check other manga in this entry
}
}
});

// Remove the entries outside the loop to avoid concurrent modification
if (keysToRemove.length > 0) {
keysToRemove.forEach((key) => {
delete mangaCache[key];
});
cleared = true;
}

// Save the updated cache if we cleared anything
if (cleared) {
saveCache();
}

return cleared;
},

/**
* Clear cache entries for multiple manga titles at once
* @param titles Array of manga titles to clear from cache
* @returns number Number of cache entries cleared
*/
clearCacheForTitles(titles: string[]): number {
if (!titles || titles.length === 0) return 0;

console.log(`Clearing cache for ${titles.length} manga titles...`);
let entriesCleared = 0;
let notFoundCount = 0;

// Process all titles in a batch
titles.forEach((title) => {
if (this.clearCacheEntryForTitle(title)) {
entriesCleared++;
} else {
notFoundCount++;
}
});

console.log(
`Cleared ${entriesCleared} cache entries (${notFoundCount} titles had no existing cache entries)`,
);
return entriesCleared;
},

clearAllCaches() {
// Clear in-memory cache
Object.keys(mangaCache).forEach((key) => {
delete mangaCache[key];
});

// Clear localStorage caches
try {
localStorage.removeItem("anilist_manga_cache");
localStorage.removeItem("anilist_search_cache");
console.log("All AniList caches cleared successfully");
} catch (e) {
console.error("Error clearing localStorage caches:", e);
}

return this.getCacheStatus();
},

printCacheKeysFor(title: string) {
const key = generateCacheKey(title);
console.log(`Cache key for "${title}": ${key}`);

// Check if we have a cache entry for this title
if (mangaCache[key]) {
console.log(
`Found in-memory cache entry for "${title}" with ${mangaCache[key].manga.length} results`,
);
} else {
console.log(`No in-memory cache entry found for "${title}"`);
}

return key;
},

dumpCache() {
return {
...mangaCache,
};
},
};