interface CacheConfig {
name: string;
validTime: number;
endpoints: string[];
options?: CacheOptions;
clearOnHide?: boolean;
}
interface CacheOptions {
ignoreSearch: boolean;
ignoreMethod: boolean;
ignoreVary: boolean;
}
const CACHE_CONFIG: CacheConfig[] = [];
const DEFAULT_CACHE_OPTIONS = {
ignoreSearch: true,
ignoreMethod: false,
ignoreVary: true,
};
const DEFAULT_SEPARATOR = '@';
async function fetchData<T>(endpoint: string): Promise<T | void> {
try {
const response: Response = await handleEndpoint(endpoint);
if (response.ok) {
return await response.json();
} else {
return handleError(response.status);
}
} catch (error) {
return handleError(error);
}
}
function handleEndpoint(endpoint: string): Promise<Response> {
const config = CACHE_CONFIG.find((config) =>
config.endpoints.some((ep) => endpoint.includes(ep))
);
if (config) {
return handleCachedEndpoint(endpoint, config);
} else {
return fetch(endpoint);
}
}
async function handleCachedEndpoint(
endpoint: string,
config: CacheConfig
): Promise<Response> {
if ('caches' in self) {
const cacheName = await getCacheName(config);
try {
if (config.clearOnHide) {
handleClearOnHide(cacheName);
}
const cache = await caches.open(cacheName);
const options = config.options ?? DEFAULT_CACHE_OPTIONS;
const response = await cache.match(endpoint, options);
if (response) {
return response;
} else {
await cache.add(endpoint);
return (await cache.match(endpoint, options)) as Response;
}
} catch (error) {
handleError(error);
return fetch(endpoint);
}
} else {
return fetch(endpoint);
}
}
async function getCacheName(config: CacheConfig): Promise<string> {
const keys = await caches.keys();
const cacheName = keys.find((key) => key.includes(config.name));
const now = getTimestamp();
if (cacheName) {
const timestamp =
Number(cacheName.split(DEFAULT_SEPARATOR)[1]) || undefined;
if (!timestamp || now - timestamp >= config.validTime) {
await caches.delete(cacheName);
return getNewCacheName(config.name, now);
} else {
return cacheName;
}
} else {
return getNewCacheName(config.name, now);
}
}
function getTimestamp(): number {
const now = new Date().getTime();
return Math.floor(now / 1000) * 1000;
}
function getNewCacheName(name: string, now: number) {
return `${name}${DEFAULT_SEPARATOR}${now}`;
}
function handleClearOnHide(cacheName: string) {
if (document) {
document.onvisibilitychange = async () => {
if (document.visibilityState === 'hidden') {
await caches.delete(cacheName);
}
};
}
}
function handleError(error: unknown): void {
console.log(error);
}
Last active
October 7, 2022 06:23
-
-
Save peplocanto/b96b4cebee594b738da85c03702d903e to your computer and use it in GitHub Desktop.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment