Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import { Cache, CacheEvents } from '@algolia/cache-common';

import { BrowserLocalStorageCacheItem, BrowserLocalStorageOptions } from '.';

function yieldToMain(): Promise<void> {
// eslint-disable-next-line no-undef
const g: any = typeof globalThis !== 'undefined' ? globalThis : undefined;

if (g && g.scheduler && g.scheduler.yield) {
return g.scheduler.yield().catch(() => {
return new Promise(resolve => setTimeout(resolve, 0));
});
}

return new Promise(resolve => setTimeout(resolve, 0));
}

export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptions): Cache {
const namespaceKey = `algoliasearch-client-js-${options.key}`;

Expand All @@ -23,30 +36,24 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio
getStorage().setItem(namespaceKey, JSON.stringify(namespace));
};

const removeOutdatedCacheItems = () => {
const getFilteredNamespace = (): Record<string, BrowserLocalStorageCacheItem> => {
const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
const namespace = getNamespace<BrowserLocalStorageCacheItem>();
const currentTime = new Date().getTime();

const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(
return Object.fromEntries(
Object.entries(namespace).filter(([, cacheItem]) => {
return cacheItem.timestamp !== undefined;
})
);
if (!cacheItem || cacheItem.timestamp === undefined) {
return false;
}

setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
if (!timeToLive) {
return true;
}

if (!timeToLive) return;

const filteredNamespaceWithoutExpiredItems = Object.fromEntries(
Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => {
const currentTimestamp = new Date().getTime();
const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp;

return !isExpired;
return cacheItem.timestamp + timeToLive >= currentTime;
})
);

setNamespace(filteredNamespaceWithoutExpiredItems);
};

return {
Expand All @@ -57,25 +64,30 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio
miss: () => Promise.resolve(),
}
): Readonly<Promise<TValue>> {
return Promise.resolve()
.then(() => {
removeOutdatedCacheItems();

const keyAsString = JSON.stringify(key);

return getNamespace<Promise<BrowserLocalStorageCacheItem>>()[keyAsString];
})
.then(value => {
return Promise.all([value ? value.value : defaultValue(), value !== undefined]);
})
.then(([value, exists]) => {
return Promise.all([value, exists || events.miss(value)]);
})
.then(([value]) => value);
return yieldToMain().then(() => {
const namespace = getFilteredNamespace();
const keyAsString = JSON.stringify(key);
const cachedItem = namespace[keyAsString];

setNamespace(namespace);

if (cachedItem) {
return cachedItem.value as TValue;
}

// eslint-disable-next-line promise/no-nesting
return defaultValue().then((value: TValue) => {
// eslint-disable-next-line promise/no-nesting
return events
.miss(value)
.catch(error => console.error(error)) // eslint-disable-line no-console
.then(() => value);
});
});
},

set<TValue>(key: object | string, value: TValue): Readonly<Promise<TValue>> {
return Promise.resolve().then(() => {
return yieldToMain().then(() => {
const namespace = getNamespace();

// eslint-disable-next-line functional/immutable-data
Expand All @@ -91,7 +103,7 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio
},

delete(key: object | string): Readonly<Promise<void>> {
return Promise.resolve().then(() => {
return yieldToMain().then(() => {
const namespace = getNamespace();

// eslint-disable-next-line functional/immutable-data
Expand Down