@kovalenko/http-request-cache
Version:
TS decorator for caching logic of API calls.
105 lines (98 loc) • 3.58 kB
JavaScript
import { Subject, merge, NEVER, startWith, switchMap, tap, shareReplay, filter, finalize } from 'rxjs';
class DefaultStorage {
constructor() {
this.storage = new Map();
}
getItem(key) {
return this.storage.get(key);
}
setItem(key, item) {
this.storage.set(key, item);
}
deleteItem(key) {
this.storage.delete(key);
}
}
class RequestTimes {
constructor() {
this.storage = new Map();
}
getItem(key) {
return this.storage.get(key);
}
setItem(key, item) {
this.storage.set(key, item);
}
deleteItem(key) {
this.storage.delete(key);
}
}
const HttpRequestCache = (optionsHandler) => {
return (target, methodName, descriptor) => {
if (!(descriptor?.value instanceof Function)) {
throw Error(`'@HttpRequestCache' can be applied only to the class method which returns an Observable`);
}
const cacheKeyPrefix = `${target.constructor.name}_${methodName}`;
const originalMethod = descriptor.value;
const working = {};
let subscribers = 0;
descriptor.value = function (...args) {
const options = optionsHandler?.call(this, this, ...args);
if (!options?.storage && !target._____storage_____) {
target._____storage_____ = new DefaultStorage();
}
if (options?.ttl && !target._____ttl_storage_____) {
target._____ttl_storage_____ = new RequestTimes();
}
const storage = options?.storage ?? target._____storage_____;
const key = `${cacheKeyPrefix}_${JSON.stringify(args)}`;
let ttl = undefined;
if (options?.ttl) {
ttl = target._____ttl_storage_____.getItem(key);
if (!ttl) {
ttl = {
requestTime: Date.now(),
subject: new Subject(),
};
}
else if (ttl.requestTime + options.ttl <= Date.now()) {
working[key] = true;
ttl.requestTime = Date.now();
ttl.subject.next();
}
target._____ttl_storage_____.setItem(key, ttl);
}
const refreshOn = merge(options?.refreshOn ?? NEVER, ttl?.subject ?? NEVER);
let observable = storage.getItem(key);
if (!observable) {
observable = refreshOn.pipe(startWith(true), switchMap(() => originalMethod.apply(this, [...args])), tap(() => {
delete working[key];
}), shareReplay({
bufferSize: 1,
refCount: options?.refCount ?? false,
windowTime: options?.windowTime ?? Infinity,
}), filter(() => {
return !working[key];
}), finalize(() => {
subscribers--;
if (subscribers === 0 && options?.refCount) {
storage.deleteItem(key);
target._____ttl_storage_____?.deleteItem(key);
}
}));
storage.setItem(key, observable);
}
subscribers++;
return observable;
};
return descriptor;
};
};
/*
* Public API Surface of http-request-cache
*/
/**
* Generated bundle index. Do not edit.
*/
export { DefaultStorage, HttpRequestCache };
//# sourceMappingURL=kovalenko-http-request-cache.mjs.map