@kovalenko/http-request-cache
Version:
TS decorator for caching logic of API calls.
107 lines (100 loc) • 4.44 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 === null || descriptor === void 0 ? void 0 : 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) {
var _a, _b, _c, _d, _e;
const options = optionsHandler === null || optionsHandler === void 0 ? void 0 : optionsHandler.call(this, this, ...args);
if (!(options === null || options === void 0 ? void 0 : options.storage) && !target._____storage_____) {
target._____storage_____ = new DefaultStorage();
}
if ((options === null || options === void 0 ? void 0 : options.ttl) && !target._____ttl_storage_____) {
target._____ttl_storage_____ = new RequestTimes();
}
const storage = (_a = options === null || options === void 0 ? void 0 : options.storage) !== null && _a !== void 0 ? _a : target._____storage_____;
const key = `${cacheKeyPrefix}_${JSON.stringify(args)}`;
let ttl = undefined;
if (options === null || options === void 0 ? void 0 : 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((_b = options === null || options === void 0 ? void 0 : options.refreshOn) !== null && _b !== void 0 ? _b : NEVER, (_c = ttl === null || ttl === void 0 ? void 0 : ttl.subject) !== null && _c !== void 0 ? _c : 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: (_d = options === null || options === void 0 ? void 0 : options.refCount) !== null && _d !== void 0 ? _d : false,
windowTime: (_e = options === null || options === void 0 ? void 0 : options.windowTime) !== null && _e !== void 0 ? _e : Infinity,
}), filter(() => {
return !working[key];
}), finalize(() => {
var _a;
subscribers--;
if (subscribers === 0 && (options === null || options === void 0 ? void 0 : options.refCount)) {
storage.deleteItem(key);
(_a = target._____ttl_storage_____) === null || _a === void 0 ? void 0 : _a.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