Generates a cache key based on the query and the bound supplier name. The limit is intentionally excluded as it only affects how many results are returned, not the actual search results themselves.
generateCacheKey(query: string): string {
const data = `${query || ""}:${this.supplierName}`;
this.logger.debug("Generating cache key with:", {
query,
supplierName: this.supplierName,
data,
});
try {
// Try browser's btoa first
const key = btoa(data);
this.logger.debug("Generated cache key:", key);
return key;
} catch {
try {
// Fallback to Node's Buffer if available
if (typeof Buffer !== "undefined") {
const key = Buffer.from(data).toString("base64");
this.logger.debug("Generated cache key (Buffer):", { key });
return key;
}
// If neither is available, use a simple hash function
let hash = 0;
for (let i = 0; i < data.length; i++) {
const char = data.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32bit integer
}
const key = hash.toString(36);
this.logger.debug("Generated cache key (hash):", { key });
return key;
} catch (error) {
this.logger.error("Error generating cache key:", error);
// Fallback to a simple string if all else fails
const key = data.replace(/[^a-zA-Z0-9]/g, "_");
this.logger.debug("Generated cache key (fallback):", { key });
return key;
}
}
}
Generates a cache key for product detail data based on the HTTP request URL, params, and the bound supplier name. This ensures that identical detail requests (even from different queries) share the same cache entry.
Optionalparams: QueryParams getProductDataCacheKey(url: string, params?: QueryParams): string {
const data = {
url, // Must match the actual HTTP request URL
params: params || {}, // Must match the actual HTTP request params
supplier: this.supplierName, // Multi-supplier safety
};
return md5(JSON.stringify(data));
}
Stores query results in the cache. LRU eviction (max 100 entries) is handled by idbCache.
async cacheQueryResults(query: string, results: unknown[], limit: number): Promise<void> {
if (!this.enabled) return;
if (this.doNotCacheEmptyResults && results.length === 0) {
this.logger.debug("Skipping empty-result cache write per doNotCacheEmptyResults", {
query,
supplierName: this.supplierName,
});
return;
}
this.logger.debug("[SupplierCache] Caching query results", {
query,
supplierName: this.supplierName,
results,
limit,
});
try {
const key = this.generateCacheKey(query);
const entry: CachedData<unknown> = {
data: results,
__cacheMetadata: {
cachedAt: Date.now(),
version: SupplierCache.CACHE_VERSION,
query,
supplier: this.supplierName,
supplierModule: this.supplierModule,
resultCount: results.length,
limit,
},
};
this.logger.debug("Cached query results", {
key,
metadata: entry.__cacheMetadata,
});
await putSupplierQueryCacheEntry(key, entry);
} catch (error) {
this.logger.error("Error storing query results in cache:", { error });
}
}
Retrieves cached product data for a given key. Updates the timestamp on access to prevent premature eviction.
async getCachedProductData(key: string): Promise<Maybe<Record<string, unknown>>> {
if (!this.enabled) return undefined;
this.logger.debug("[SupplierCache] Getting cached product data", { key });
try {
const cached = await getSupplierProductDataCacheEntry(key);
if (cached) {
// Refresh timestamp on access
await putSupplierProductDataCacheEntry(key, {
data: cached.data,
timestamp: Date.now(),
});
return cached.data;
}
return undefined;
} catch (error) {
this.logger.error("Error retrieving product data from cache:", { error });
return undefined;
}
}
Stores product data in the cache. LRU eviction (max 100 entries) is handled by idbCache.
async cacheProductData(key: string, data: Record<string, unknown>): Promise<void> {
if (!this.enabled) return;
this.logger.debug("Caching product data", { key, data });
try {
await putSupplierProductDataCacheEntry(key, {
data,
timestamp: Date.now(),
});
} catch (error) {
this.logger.error("Error storing product data in cache:", { error });
}
}
Retrieves a cached query entry by key.
async getCachedQueryEntry(key: string): Promise<CachedData<unknown> | undefined> {
if (!this.enabled) return undefined;
this.logger.debug("Getting cached query entry", { key });
try {
const cached = await getSupplierQueryCacheEntry(key);
if (cached && cached.__cacheMetadata.version !== SupplierCache.CACHE_VERSION) {
this.logger.debug("Evicting stale cache entry due to version mismatch", {
key,
cachedVersion: cached.__cacheMetadata.version,
currentVersion: SupplierCache.CACHE_VERSION,
});
await deleteSupplierQueryCacheEntry(key);
return undefined;
}
if (cached && this.cacheTtlMs > 0) {
const age = Date.now() - cached.__cacheMetadata.cachedAt;
if (age > this.cacheTtlMs) {
this.logger.debug("Evicting expired cache entry due to TTL", {
key,
ageMs: age,
ttlMs: this.cacheTtlMs,
});
await deleteSupplierQueryCacheEntry(key);
return undefined;
}
}
return cached;
} catch (error) {
this.logger.error("Error retrieving query cache entry:", { error });
return undefined;
}
}
StaticclearClears both the query cache and product data cache from IndexedDB.
static async clearAll(): Promise<void> {
await Promise.all([clearSupplierQueryCache(), clearSupplierProductDataCache()]);
}
Utility class for managing supplier data caching in IndexedDB. Provides a robust caching system for both query results and product detail data.
Remarks
The cache system uses two IndexedDB object stores:
supplierQueryCache- Stores search query results (one record per query+supplier)supplierProductDataCache- Stores detailed product data (one record per product)Each store supports max 100 entries with LRU eviction handled by idbCache.
Type Param: T
The type of data being cached
Source