async-source
Advanced tools
Comparing version 1.1.9 to 2.0.0
@@ -0,1 +1,19 @@ | ||
interface AsyncStorage { | ||
getItem: (key: string) => Promise<string | undefined | null>; | ||
setItem: (key: string, value: string) => Promise<unknown>; | ||
removeItem: (key: string) => Promise<void>; | ||
} | ||
interface AsyncSourceConfig { | ||
cacheStorage?: CacheStorage; | ||
cacheTime?: number; | ||
cachePrefix?: string; | ||
} | ||
interface AsyncSourceInstanceConfig { | ||
debounceTime?: number; | ||
requestCacheKey?: string; | ||
cacheTime?: number; | ||
cacheStorage?: CacheStorage; | ||
isUpdateCache?: boolean; | ||
} | ||
type CacheStorage = AsyncStorage | Storage | undefined | null; | ||
export type ResponseData<T> = T; | ||
@@ -12,3 +30,14 @@ export type ServiceMethod<T = any> = (...args: Array<any>) => Promise<T>; | ||
private lastRequestId; | ||
private requestCacheKey?; | ||
private isCacheEnabled; | ||
private cacheTime; | ||
private cacheStorage; | ||
private isUpdateCache; | ||
private cacheKey?; | ||
private static defaultCacheTime; | ||
private static defaultStorage; | ||
private static cachePrefix; | ||
constructor(serviceMethod: ServiceMethod<T>, errorHandler?: ErrorHandler, config?: AsyncSourceInstanceConfig); | ||
constructor(serviceMethod: ServiceMethod<T>, errorHandler?: ErrorHandler, debounceTime?: number); | ||
static setConfig(config: AsyncSourceConfig): void; | ||
get data(): ResponseData<T>; | ||
@@ -23,2 +52,7 @@ get isLoading(): boolean; | ||
clear(): void; | ||
private syncCache; | ||
private updateCacheKey; | ||
private removeCachedData; | ||
private getCachedData; | ||
private setCachedData; | ||
private request; | ||
@@ -25,0 +59,0 @@ private createRequestId; |
@@ -40,5 +40,5 @@ "use strict"; | ||
var AsyncSource = /** @class */ (function () { | ||
function AsyncSource(serviceMethod, errorHandler, debounceTime) { | ||
function AsyncSource(serviceMethod, errorHandler, debounceTimeOrConfig) { | ||
if (errorHandler === void 0) { errorHandler = function () { }; } | ||
if (debounceTime === void 0) { debounceTime = 100; } | ||
if (debounceTimeOrConfig === void 0) { debounceTimeOrConfig = 100; } | ||
this.responseData = null; | ||
@@ -48,6 +48,32 @@ this.isRequestPending = false; | ||
this.lastRequestId = 0; | ||
this.isCacheEnabled = false; | ||
this.cacheStorage = null; | ||
this.isUpdateCache = true; | ||
this.serviceMethod = serviceMethod; | ||
this.debounceTime = debounceTime; | ||
this.onError = errorHandler; | ||
if (typeof debounceTimeOrConfig === 'number') { | ||
this.debounceTime = debounceTimeOrConfig; | ||
} | ||
else { | ||
var _a = debounceTimeOrConfig.debounceTime, debounceTime = _a === void 0 ? 100 : _a, requestCacheKey = debounceTimeOrConfig.requestCacheKey, _b = debounceTimeOrConfig.cacheTime, cacheTime = _b === void 0 ? AsyncSource.defaultCacheTime : _b, _c = debounceTimeOrConfig.cacheStorage, cacheStorage = _c === void 0 ? AsyncSource.defaultStorage : _c, _d = debounceTimeOrConfig.isUpdateCache, isUpdateCache = _d === void 0 ? true : _d; | ||
this.debounceTime = debounceTime; | ||
this.isCacheEnabled = Boolean(requestCacheKey); | ||
this.requestCacheKey = requestCacheKey; | ||
this.cacheTime = cacheTime; | ||
this.cacheStorage = cacheStorage; | ||
this.isUpdateCache = isUpdateCache; | ||
} | ||
} | ||
// Set cache config | ||
AsyncSource.setConfig = function (config) { | ||
if (config.cacheStorage) { | ||
AsyncSource.defaultStorage = config.cacheStorage; | ||
} | ||
if (typeof config.cacheTime === 'number') { | ||
AsyncSource.defaultCacheTime = config.cacheTime; | ||
} | ||
if (config.cachePrefix) { | ||
AsyncSource.cachePrefix = config.cachePrefix; | ||
} | ||
}; | ||
Object.defineProperty(AsyncSource.prototype, "data", { | ||
@@ -180,7 +206,126 @@ // Response data getter | ||
this.lastRequestId = 0; | ||
this.removeCachedData(); | ||
}; | ||
AsyncSource.prototype.syncCache = function (args) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var cachedData; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
this.updateCacheKey(args); | ||
return [4 /*yield*/, this.getCachedData()]; | ||
case 1: | ||
cachedData = _a.sent(); | ||
if (cachedData) { | ||
this.responseData = cachedData; | ||
this.isFetchedData = true; | ||
this.isRequestPending = false; | ||
} | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
AsyncSource.prototype.updateCacheKey = function (requestParams) { | ||
var baseCacheKey = "".concat(AsyncSource.cachePrefix, "-").concat(this.requestCacheKey); | ||
var isRequestParamsExist = Boolean(requestParams.length); | ||
var requestParamsHash; | ||
if (isRequestParamsExist) { | ||
try { | ||
requestParamsHash = JSON.stringify(requestParams); | ||
} | ||
catch (error) { | ||
console.warn({ message: "Request params saving error cacheKey:".concat(baseCacheKey), params: requestParams, error: error }); | ||
} | ||
} | ||
this.cacheKey = requestParamsHash ? "".concat(baseCacheKey, "-").concat(requestParamsHash) : baseCacheKey; | ||
}; | ||
AsyncSource.prototype.removeCachedData = function () { | ||
var _a, _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var error_1; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
if (!this.cacheKey) | ||
return [2 /*return*/]; | ||
_c.label = 1; | ||
case 1: | ||
_c.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, ((_b = (_a = this.cacheStorage) === null || _a === void 0 ? void 0 : _a.removeItem) === null || _b === void 0 ? void 0 : _b.call(_a, this.cacheKey))]; | ||
case 2: | ||
_c.sent(); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
error_1 = _c.sent(); | ||
console.warn({ message: "Cache removing error cacheKey:".concat(this.cacheKey), error: error_1 }); | ||
return [3 /*break*/, 4]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
AsyncSource.prototype.getCachedData = function () { | ||
var _a, _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var storedData, _c, data, timestamp, isExpired, error_2; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
if (!this.cacheKey) | ||
return [2 /*return*/, null]; | ||
_d.label = 1; | ||
case 1: | ||
_d.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, ((_b = (_a = this.cacheStorage) === null || _a === void 0 ? void 0 : _a.getItem) === null || _b === void 0 ? void 0 : _b.call(_a, this.cacheKey))]; | ||
case 2: | ||
storedData = _d.sent(); | ||
if (storedData) { | ||
_c = JSON.parse(storedData), data = _c.data, timestamp = _c.timestamp; | ||
isExpired = Date.now() - timestamp > this.cacheTime; | ||
return [2 /*return*/, isExpired ? null : data]; | ||
} | ||
return [2 /*return*/, null]; | ||
case 3: | ||
error_2 = _d.sent(); | ||
console.warn({ message: "Cache getting error cacheKey:".concat(this.cacheKey), error: error_2 }); | ||
return [2 /*return*/, null]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
AsyncSource.prototype.setCachedData = function (data) { | ||
var _a, _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var cacheValue, error_3; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
if (!this.cacheKey) | ||
return [2 /*return*/]; | ||
cacheValue = { | ||
data: data, | ||
timestamp: Date.now() | ||
}; | ||
_c.label = 1; | ||
case 1: | ||
_c.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, ((_b = (_a = this.cacheStorage) === null || _a === void 0 ? void 0 : _a.setItem) === null || _b === void 0 ? void 0 : _b.call(_a, this.cacheKey, JSON.stringify(cacheValue)))]; | ||
case 2: | ||
_c.sent(); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
error_3 = _c.sent(); | ||
console.warn({ message: "Cache saving error cacheKey:".concat(this.cacheKey), error: error_3 }); | ||
return [3 /*break*/, 4]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
// Core request method | ||
AsyncSource.prototype.request = function (args, successHandler, isImmediate) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var requestId, response, error_1; | ||
var requestId, response, error_4; | ||
return __generator(this, function (_a) { | ||
@@ -195,7 +340,18 @@ switch (_a.label) { | ||
return [2 /*return*/]; | ||
_a.label = 2; | ||
if (!this.isCacheEnabled) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, this.syncCache(args)]; | ||
case 2: | ||
_a.trys.push([2, 4, , 5]); | ||
_a.sent(); | ||
if (this.responseData && !this.isRequestPending) { | ||
this.isFetchedData = true; | ||
successHandler === null || successHandler === void 0 ? void 0 : successHandler(this.responseData); | ||
if (!this.isUpdateCache) { | ||
return [2 /*return*/]; | ||
} | ||
} | ||
_a.label = 3; | ||
case 3: | ||
_a.trys.push([3, 5, , 6]); | ||
return [4 /*yield*/, this.serviceMethod.apply(this, args)]; | ||
case 3: | ||
case 4: | ||
response = _a.sent(); | ||
@@ -207,6 +363,9 @@ if (this.isLastRequest(requestId)) { | ||
successHandler === null || successHandler === void 0 ? void 0 : successHandler(response); | ||
if (this.isCacheEnabled) { | ||
this.setCachedData(response); | ||
} | ||
} | ||
return [3 /*break*/, 5]; | ||
case 4: | ||
error_1 = _a.sent(); | ||
return [3 /*break*/, 6]; | ||
case 5: | ||
error_4 = _a.sent(); | ||
if (this.isLastRequest(requestId)) { | ||
@@ -216,6 +375,6 @@ this.isRequestPending = false; | ||
this.responseData = null; | ||
this.onError(error_1); | ||
this.onError(error_4); | ||
} | ||
return [3 /*break*/, 5]; | ||
case 5: return [2 /*return*/]; | ||
return [3 /*break*/, 6]; | ||
case 6: return [2 /*return*/]; | ||
} | ||
@@ -233,5 +392,3 @@ }); | ||
} | ||
return new Promise(function (resolve) { return setTimeout(function () { | ||
resolve(requestId); | ||
}, _this.debounceTime); }); | ||
return new Promise(function (resolve) { return setTimeout(resolve, _this.debounceTime, requestId); }); | ||
}; | ||
@@ -241,2 +398,5 @@ AsyncSource.prototype.isLastRequest = function (requestId) { | ||
}; | ||
AsyncSource.defaultCacheTime = 12 * 60 * 60 * 1000; | ||
AsyncSource.defaultStorage = localStorage; | ||
AsyncSource.cachePrefix = 'AsyncSource'; | ||
return AsyncSource; | ||
@@ -243,0 +403,0 @@ }()); |
@@ -1,1 +0,1 @@ | ||
"use strict";var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve){resolve(value)})}return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=this&&this.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(g&&(g=0,op[0]&&(_=0)),_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]<t[3])){_.label=op[1];break}if(op[0]===6&&_.label<t[1]){_.label=t[1];t=op;break}if(t&&_.label<t[2]){_.label=t[2];_.ops.push(op);break}if(t[2])_.ops.pop();_.trys.pop();continue}op=body.call(thisArg,_)}catch(e){op=[6,e];y=0}finally{f=t=0}if(op[0]&5)throw op[1];return{value:op[0]?op[1]:void 0,done:true}}};Object.defineProperty(exports,"__esModule",{value:true});var AsyncSource=function(){function AsyncSource(serviceMethod,errorHandler,debounceTime){if(errorHandler===void 0){errorHandler=function(){}}if(debounceTime===void 0){debounceTime=100}this.responseData=null;this.isRequestPending=false;this.isFetchedData=false;this.lastRequestId=0;this.serviceMethod=serviceMethod;this.debounceTime=debounceTime;this.onError=errorHandler}Object.defineProperty(AsyncSource.prototype,"data",{get:function(){return this.responseData},enumerable:false,configurable:true});Object.defineProperty(AsyncSource.prototype,"isLoading",{get:function(){return this.isRequestPending},enumerable:false,configurable:true});Object.defineProperty(AsyncSource.prototype,"isFetch",{get:function(){return this.isFetchedData},enumerable:false,configurable:true});AsyncSource.prototype.update=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:return[4,this.request(args)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.updateIfEmpty=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:if(this.data)return[2];return[4,this.request(args)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.updateOnce=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:if(!this.isLoading)return[3,3];return[4,new Promise(function(resolve){return setTimeout(resolve,100)})];case 1:_a.sent();return[4,this.updateOnce.apply(this,args)];case 2:_a.sent();return[3,5];case 3:return[4,this.updateIfEmpty.apply(this,args)];case 4:_a.sent();_a.label=5;case 5:return[2]}})})};AsyncSource.prototype.updateImmediate=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:return[4,this.request(args,undefined,true)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.push=function(successHandler){var args=[];for(var _i=1;_i<arguments.length;_i++){args[_i-1]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:return[4,this.request(args,successHandler)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.clear=function(){this.isRequestPending=false;this.isFetchedData=false;this.responseData=null;this.lastRequestId=0};AsyncSource.prototype.request=function(args,successHandler,isImmediate){return __awaiter(this,void 0,void 0,function(){var requestId,response,error_1;return __generator(this,function(_a){switch(_a.label){case 0:this.isRequestPending=true;return[4,this.createRequestId(isImmediate)];case 1:requestId=_a.sent();if(!this.isLastRequest(requestId))return[2];_a.label=2;case 2:_a.trys.push([2,4,,5]);return[4,this.serviceMethod.apply(this,args)];case 3:response=_a.sent();if(this.isLastRequest(requestId)){this.isRequestPending=false;this.isFetchedData=true;this.responseData=response;successHandler===null||successHandler===void 0?void 0:successHandler(response)}return[3,5];case 4:error_1=_a.sent();if(this.isLastRequest(requestId)){this.isRequestPending=false;this.isFetchedData=true;this.responseData=null;this.onError(error_1)}return[3,5];case 5:return[2]}})})};AsyncSource.prototype.createRequestId=function(isImmediate){var _this=this;var isFirstRequest=!this.lastRequestId;var requestId=this.lastRequestId+1;this.lastRequestId=requestId;if(isFirstRequest||isImmediate){return Promise.resolve(requestId)}return new Promise(function(resolve){return setTimeout(function(){resolve(requestId)},_this.debounceTime)})};AsyncSource.prototype.isLastRequest=function(requestId){return requestId===this.lastRequestId};return AsyncSource}();exports.default=AsyncSource; | ||
"use strict";var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve){resolve(value)})}return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=this&&this.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(g&&(g=0,op[0]&&(_=0)),_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]<t[3])){_.label=op[1];break}if(op[0]===6&&_.label<t[1]){_.label=t[1];t=op;break}if(t&&_.label<t[2]){_.label=t[2];_.ops.push(op);break}if(t[2])_.ops.pop();_.trys.pop();continue}op=body.call(thisArg,_)}catch(e){op=[6,e];y=0}finally{f=t=0}if(op[0]&5)throw op[1];return{value:op[0]?op[1]:void 0,done:true}}};Object.defineProperty(exports,"__esModule",{value:true});var AsyncSource=function(){function AsyncSource(serviceMethod,errorHandler,debounceTimeOrConfig){if(errorHandler===void 0){errorHandler=function(){}}if(debounceTimeOrConfig===void 0){debounceTimeOrConfig=100}this.responseData=null;this.isRequestPending=false;this.isFetchedData=false;this.lastRequestId=0;this.isCacheEnabled=false;this.cacheStorage=null;this.isUpdateCache=true;this.serviceMethod=serviceMethod;this.onError=errorHandler;if(typeof debounceTimeOrConfig==="number"){this.debounceTime=debounceTimeOrConfig}else{var _a=debounceTimeOrConfig.debounceTime,debounceTime=_a===void 0?100:_a,requestCacheKey=debounceTimeOrConfig.requestCacheKey,_b=debounceTimeOrConfig.cacheTime,cacheTime=_b===void 0?AsyncSource.defaultCacheTime:_b,_c=debounceTimeOrConfig.cacheStorage,cacheStorage=_c===void 0?AsyncSource.defaultStorage:_c,_d=debounceTimeOrConfig.isUpdateCache,isUpdateCache=_d===void 0?true:_d;this.debounceTime=debounceTime;this.isCacheEnabled=Boolean(requestCacheKey);this.requestCacheKey=requestCacheKey;this.cacheTime=cacheTime;this.cacheStorage=cacheStorage;this.isUpdateCache=isUpdateCache}}AsyncSource.setConfig=function(config){if(config.cacheStorage){AsyncSource.defaultStorage=config.cacheStorage}if(typeof config.cacheTime==="number"){AsyncSource.defaultCacheTime=config.cacheTime}if(config.cachePrefix){AsyncSource.cachePrefix=config.cachePrefix}};Object.defineProperty(AsyncSource.prototype,"data",{get:function(){return this.responseData},enumerable:false,configurable:true});Object.defineProperty(AsyncSource.prototype,"isLoading",{get:function(){return this.isRequestPending},enumerable:false,configurable:true});Object.defineProperty(AsyncSource.prototype,"isFetch",{get:function(){return this.isFetchedData},enumerable:false,configurable:true});AsyncSource.prototype.update=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:return[4,this.request(args)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.updateIfEmpty=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:if(this.data)return[2];return[4,this.request(args)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.updateOnce=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:if(!this.isLoading)return[3,3];return[4,new Promise(function(resolve){return setTimeout(resolve,100)})];case 1:_a.sent();return[4,this.updateOnce.apply(this,args)];case 2:_a.sent();return[3,5];case 3:return[4,this.updateIfEmpty.apply(this,args)];case 4:_a.sent();_a.label=5;case 5:return[2]}})})};AsyncSource.prototype.updateImmediate=function(){var args=[];for(var _i=0;_i<arguments.length;_i++){args[_i]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:return[4,this.request(args,undefined,true)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.push=function(successHandler){var args=[];for(var _i=1;_i<arguments.length;_i++){args[_i-1]=arguments[_i]}return __awaiter(this,void 0,void 0,function(){return __generator(this,function(_a){switch(_a.label){case 0:return[4,this.request(args,successHandler)];case 1:_a.sent();return[2]}})})};AsyncSource.prototype.clear=function(){this.isRequestPending=false;this.isFetchedData=false;this.responseData=null;this.lastRequestId=0;this.removeCachedData()};AsyncSource.prototype.syncCache=function(args){return __awaiter(this,void 0,void 0,function(){var cachedData;return __generator(this,function(_a){switch(_a.label){case 0:this.updateCacheKey(args);return[4,this.getCachedData()];case 1:cachedData=_a.sent();if(cachedData){this.responseData=cachedData;this.isFetchedData=true;this.isRequestPending=false}return[2]}})})};AsyncSource.prototype.updateCacheKey=function(requestParams){var baseCacheKey="".concat(AsyncSource.cachePrefix,"-").concat(this.requestCacheKey);var isRequestParamsExist=Boolean(requestParams.length);var requestParamsHash;if(isRequestParamsExist){try{requestParamsHash=JSON.stringify(requestParams)}catch(error){console.warn({message:"Request params saving error cacheKey:".concat(baseCacheKey),params:requestParams,error:error})}}this.cacheKey=requestParamsHash?"".concat(baseCacheKey,"-").concat(requestParamsHash):baseCacheKey};AsyncSource.prototype.removeCachedData=function(){var _a,_b;return __awaiter(this,void 0,void 0,function(){var error_1;return __generator(this,function(_c){switch(_c.label){case 0:if(!this.cacheKey)return[2];_c.label=1;case 1:_c.trys.push([1,3,,4]);return[4,(_b=(_a=this.cacheStorage)===null||_a===void 0?void 0:_a.removeItem)===null||_b===void 0?void 0:_b.call(_a,this.cacheKey)];case 2:_c.sent();return[3,4];case 3:error_1=_c.sent();console.warn({message:"Cache removing error cacheKey:".concat(this.cacheKey),error:error_1});return[3,4];case 4:return[2]}})})};AsyncSource.prototype.getCachedData=function(){var _a,_b;return __awaiter(this,void 0,void 0,function(){var storedData,_c,data,timestamp,isExpired,error_2;return __generator(this,function(_d){switch(_d.label){case 0:if(!this.cacheKey)return[2,null];_d.label=1;case 1:_d.trys.push([1,3,,4]);return[4,(_b=(_a=this.cacheStorage)===null||_a===void 0?void 0:_a.getItem)===null||_b===void 0?void 0:_b.call(_a,this.cacheKey)];case 2:storedData=_d.sent();if(storedData){_c=JSON.parse(storedData),data=_c.data,timestamp=_c.timestamp;isExpired=Date.now()-timestamp>this.cacheTime;return[2,isExpired?null:data]}return[2,null];case 3:error_2=_d.sent();console.warn({message:"Cache getting error cacheKey:".concat(this.cacheKey),error:error_2});return[2,null];case 4:return[2]}})})};AsyncSource.prototype.setCachedData=function(data){var _a,_b;return __awaiter(this,void 0,void 0,function(){var cacheValue,error_3;return __generator(this,function(_c){switch(_c.label){case 0:if(!this.cacheKey)return[2];cacheValue={data:data,timestamp:Date.now()};_c.label=1;case 1:_c.trys.push([1,3,,4]);return[4,(_b=(_a=this.cacheStorage)===null||_a===void 0?void 0:_a.setItem)===null||_b===void 0?void 0:_b.call(_a,this.cacheKey,JSON.stringify(cacheValue))];case 2:_c.sent();return[3,4];case 3:error_3=_c.sent();console.warn({message:"Cache saving error cacheKey:".concat(this.cacheKey),error:error_3});return[3,4];case 4:return[2]}})})};AsyncSource.prototype.request=function(args,successHandler,isImmediate){return __awaiter(this,void 0,void 0,function(){var requestId,response,error_4;return __generator(this,function(_a){switch(_a.label){case 0:this.isRequestPending=true;return[4,this.createRequestId(isImmediate)];case 1:requestId=_a.sent();if(!this.isLastRequest(requestId))return[2];if(!this.isCacheEnabled)return[3,3];return[4,this.syncCache(args)];case 2:_a.sent();if(this.responseData&&!this.isRequestPending){this.isFetchedData=true;successHandler===null||successHandler===void 0?void 0:successHandler(this.responseData);if(!this.isUpdateCache){return[2]}}_a.label=3;case 3:_a.trys.push([3,5,,6]);return[4,this.serviceMethod.apply(this,args)];case 4:response=_a.sent();if(this.isLastRequest(requestId)){this.isRequestPending=false;this.isFetchedData=true;this.responseData=response;successHandler===null||successHandler===void 0?void 0:successHandler(response);if(this.isCacheEnabled){this.setCachedData(response)}}return[3,6];case 5:error_4=_a.sent();if(this.isLastRequest(requestId)){this.isRequestPending=false;this.isFetchedData=true;this.responseData=null;this.onError(error_4)}return[3,6];case 6:return[2]}})})};AsyncSource.prototype.createRequestId=function(isImmediate){var _this=this;var isFirstRequest=!this.lastRequestId;var requestId=this.lastRequestId+1;this.lastRequestId=requestId;if(isFirstRequest||isImmediate){return Promise.resolve(requestId)}return new Promise(function(resolve){return setTimeout(resolve,_this.debounceTime,requestId)})};AsyncSource.prototype.isLastRequest=function(requestId){return requestId===this.lastRequestId};AsyncSource.defaultCacheTime=12*60*60*1e3;AsyncSource.defaultStorage=localStorage;AsyncSource.cachePrefix="AsyncSource";return AsyncSource}();exports.default=AsyncSource; |
@@ -88,2 +88,7 @@ "use strict"; | ||
var mockedMethodWithError = vitest_1.vi.fn(function (error) { return Promise.reject(error); }); | ||
var createMockStorage = function () { return ({ | ||
getItem: vitest_1.vi.fn().mockImplementation(function () { return Promise.resolve(null); }), | ||
setItem: vitest_1.vi.fn().mockImplementation(function () { return Promise.resolve(); }), | ||
removeItem: vitest_1.vi.fn().mockImplementation(function () { return Promise.resolve(); }), | ||
}); }; | ||
// tests | ||
@@ -406,3 +411,344 @@ var sut; | ||
}); | ||
(0, vitest_1.describe)("AsyncSource Cache Functionality", function () { | ||
var mockStorage; | ||
(0, vitest_1.beforeEach)(function () { | ||
mockStorage = createMockStorage(); | ||
localStorage.clear(); | ||
}); | ||
(0, vitest_1.afterEach)(function () { | ||
vitest_1.vi.clearAllMocks(); | ||
}); | ||
(0, vitest_1.it)("should save data to cache after successful fetch", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var response, serviceMethod, asyncSource, _a, key, value, parsedValue; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
response = { data: "test data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(response); }); | ||
asyncSource = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "testKey", | ||
cacheStorage: mockStorage, | ||
cacheTime: 1000, | ||
isUpdateCache: true | ||
}); | ||
return [4 /*yield*/, asyncSource.update()]; | ||
case 1: | ||
_b.sent(); | ||
(0, vitest_1.expect)(mockStorage.setItem).toHaveBeenCalledOnce(); | ||
_a = mockStorage.setItem.mock.calls[0], key = _a[0], value = _a[1]; | ||
(0, vitest_1.expect)(key).toContain("AsyncSource-testKey"); | ||
parsedValue = JSON.parse(value); | ||
(0, vitest_1.expect)(parsedValue.data).toEqual(response); | ||
(0, vitest_1.expect)(parsedValue.timestamp).toBeGreaterThan(0); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should retrieve data from cache if available and not expired", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var cachedResponse; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
cachedResponse = { data: { data: "cached data" }, timestamp: Date.now() }; | ||
mockStorage.getItem.mockResolvedValueOnce(JSON.stringify(cachedResponse)); | ||
sut = new index_1.default(vitest_1.vi.fn(), vitest_1.vi.fn(), { | ||
requestCacheKey: "testKey", | ||
cacheStorage: mockStorage, | ||
cacheTime: 1000 | ||
}); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
(0, vitest_1.expect)(mockStorage.getItem).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(mockStorage.getItem).toHaveBeenCalledWith(vitest_1.expect.stringContaining("AsyncSource-testKey")); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should not retrieve data from cache if expired", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var expiredTimestamp, cachedResponse, response, serviceMethod; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
expiredTimestamp = Date.now() - 2000; | ||
cachedResponse = { data: { data: "cached data" }, timestamp: expiredTimestamp }; | ||
mockStorage.getItem.mockResolvedValueOnce(JSON.stringify(cachedResponse)); | ||
response = { data: "fresh data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(response); }); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "expired", | ||
cacheStorage: mockStorage, | ||
cacheTime: 1000 | ||
}); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
(0, vitest_1.expect)(sut.data).toEqual(response); | ||
(0, vitest_1.expect)(mockStorage.setItem).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledOnce(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should clear cache when calling clear method", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
sut = new index_1.default(vitest_1.vi.fn(), vitest_1.vi.fn(), { | ||
requestCacheKey: "clear", | ||
cacheStorage: mockStorage | ||
}); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
sut.clear(); | ||
(0, vitest_1.expect)(mockStorage.removeItem).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(mockStorage.removeItem).toHaveBeenCalledWith(vitest_1.expect.stringContaining("AsyncSource-clear")); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should use default storage if none provided", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var response, serviceMethod, defaultStorageSetItemSpy, _a, key1, value1; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
response = { data: "default storage data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(response); }); | ||
defaultStorageSetItemSpy = vitest_1.vi.spyOn(Storage.prototype, "setItem"); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "defaultStorage", | ||
cacheTime: 1000, | ||
}); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_b.sent(); | ||
_a = defaultStorageSetItemSpy.mock.calls[0], key1 = _a[0], value1 = _a[1]; | ||
(0, vitest_1.expect)(defaultStorageSetItemSpy).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(key1).toContain("AsyncSource-defaultStorage"); | ||
(0, vitest_1.expect)(JSON.parse(value1)).toEqual(vitest_1.expect.objectContaining({ | ||
data: response, | ||
timestamp: vitest_1.expect.any(Number) | ||
})); | ||
defaultStorageSetItemSpy.mockRestore(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should create two cache records with diff args", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var response, serviceMethod, _a, key1, value1, _b, key2, value2; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
response = { data: "default storage data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(response); }); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "defaultKey", | ||
cacheStorage: mockStorage, | ||
cacheTime: 1000, | ||
}); | ||
return [4 /*yield*/, sut.update(1)]; | ||
case 1: | ||
_c.sent(); | ||
return [4 /*yield*/, sut.update(2)]; | ||
case 2: | ||
_c.sent(); | ||
_a = mockStorage.setItem.mock.calls[0], key1 = _a[0], value1 = _a[1]; | ||
_b = mockStorage.setItem.mock.calls[1], key2 = _b[0], value2 = _b[1]; | ||
(0, vitest_1.expect)(key1).toContain("AsyncSource-defaultKey-[1]"); | ||
(0, vitest_1.expect)(JSON.parse(value1)).toEqual(vitest_1.expect.objectContaining({ | ||
data: response, | ||
timestamp: vitest_1.expect.any(Number) | ||
})); | ||
(0, vitest_1.expect)(key2).toContain("AsyncSource-defaultKey-[2]"); | ||
(0, vitest_1.expect)(JSON.parse(value2)).toEqual(vitest_1.expect.objectContaining({ | ||
data: response, | ||
timestamp: vitest_1.expect.any(Number) | ||
})); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should update cache when isUpdateCache is true", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var initialResponse, serviceMethod, defaultStorageSetItemSpy, defaultStorageGetItemSpy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
initialResponse = { data: "initial data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(initialResponse); }); | ||
defaultStorageSetItemSpy = vitest_1.vi.spyOn(Storage.prototype, "setItem"); | ||
defaultStorageGetItemSpy = vitest_1.vi.spyOn(Storage.prototype, "getItem"); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "updateCacheKey", | ||
cacheTime: 1000, | ||
isUpdateCache: true, | ||
}); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
return [4 /*yield*/, sut.update()]; | ||
case 2: | ||
_a.sent(); | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledTimes(2); | ||
(0, vitest_1.expect)(defaultStorageGetItemSpy).toHaveBeenCalledTimes(2); | ||
(0, vitest_1.expect)(defaultStorageSetItemSpy).toHaveBeenCalledTimes(2); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should not update cache when isUpdateCache is false", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var initialResponse, serviceMethod, defaultStorageSetItemSpy, defaultStorageGetItemSpy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
initialResponse = { data: "initial data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(initialResponse); }); | ||
defaultStorageSetItemSpy = vitest_1.vi.spyOn(Storage.prototype, "setItem"); | ||
defaultStorageGetItemSpy = vitest_1.vi.spyOn(Storage.prototype, "getItem"); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "notUpdateCacheKey", | ||
isUpdateCache: false, | ||
}); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
return [4 /*yield*/, sut.update()]; | ||
case 2: | ||
_a.sent(); | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(defaultStorageSetItemSpy).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(defaultStorageGetItemSpy).toHaveBeenCalledTimes(2); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
}); | ||
(0, vitest_1.describe)("AsyncSource Cache Exception Handling", function () { | ||
var mockStorage; | ||
var sut; | ||
(0, vitest_1.beforeEach)(function () { | ||
mockStorage = createMockStorage(); | ||
localStorage.clear(); | ||
}); | ||
(0, vitest_1.afterEach)(function () { | ||
vitest_1.vi.clearAllMocks(); | ||
}); | ||
(0, vitest_1.it)("should handle JSON.stringify error in updateCacheKey", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var circularReference, initialResponse, serviceMethod, consoleWarnSpy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
circularReference = {}; | ||
circularReference.self = circularReference; | ||
initialResponse = { data: "initial data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(initialResponse); }); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "testCircular", | ||
cacheStorage: mockStorage | ||
}); | ||
consoleWarnSpy = vitest_1.vi.spyOn(console, "warn"); | ||
return [4 /*yield*/, sut.update(circularReference)]; | ||
case 1: | ||
_a.sent(); | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(consoleWarnSpy).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ | ||
message: "Request params saving error cacheKey:AsyncSource-testCircular", | ||
error: vitest_1.expect.any(Error) | ||
})); | ||
consoleWarnSpy.mockRestore(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should handle removeItem error in removeCachedData", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var initialResponse, serviceMethod, consoleWarnSpy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
initialResponse = { data: "initial data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(initialResponse); }); | ||
mockStorage.removeItem.mockRejectedValueOnce(new Error("removeItem error")); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "testRemoveError", | ||
cacheStorage: mockStorage | ||
}); | ||
consoleWarnSpy = vitest_1.vi.spyOn(console, "warn"); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
sut.clear(); | ||
setTimeout(function () { | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(consoleWarnSpy).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ | ||
message: "Cache removing error cacheKey:AsyncSource-testRemoveError", | ||
error: vitest_1.expect.any(Error) | ||
})); | ||
consoleWarnSpy.mockRestore(); | ||
}, 1000); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should handle getItem error in getCachedData", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var initialResponse, serviceMethod, consoleWarnSpy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
initialResponse = { data: "initial data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(initialResponse); }); | ||
mockStorage.getItem.mockRejectedValueOnce(new Error("getItem error")); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "testGetError", | ||
cacheStorage: mockStorage, | ||
cacheTime: 1000 | ||
}); | ||
sut["cacheKey"] = "AsyncSource-testGetError"; | ||
consoleWarnSpy = vitest_1.vi.spyOn(console, "warn"); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
return [4 /*yield*/, sut.update()]; | ||
case 2: | ||
_a.sent(); | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledTimes(2); | ||
(0, vitest_1.expect)(consoleWarnSpy).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ | ||
message: "Cache getting error cacheKey:AsyncSource-testGetError", | ||
error: vitest_1.expect.any(Error) | ||
})); | ||
consoleWarnSpy.mockRestore(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
(0, vitest_1.it)("should handle setItem error in setCachedData", function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var initialResponse, serviceMethod, consoleWarnSpy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
initialResponse = { data: "initial data" }; | ||
serviceMethod = vitest_1.vi.fn(function () { return Promise.resolve(initialResponse); }); | ||
mockStorage.setItem.mockRejectedValueOnce(new Error("setItem error")); | ||
sut = new index_1.default(serviceMethod, vitest_1.vi.fn(), { | ||
requestCacheKey: "testSetError", | ||
cacheStorage: mockStorage, | ||
cacheTime: 1000 | ||
}); | ||
consoleWarnSpy = vitest_1.vi.spyOn(console, "warn"); | ||
return [4 /*yield*/, sut.update()]; | ||
case 1: | ||
_a.sent(); | ||
setTimeout(function () { | ||
(0, vitest_1.expect)(serviceMethod).toHaveBeenCalledOnce(); | ||
(0, vitest_1.expect)(consoleWarnSpy).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ | ||
message: 'Cache saving error cacheKey:AsyncSource-testSetError', | ||
error: vitest_1.expect.any(Error) | ||
})); | ||
consoleWarnSpy.mockRestore(); | ||
}, 1000); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
}); | ||
}); | ||
//# sourceMappingURL=index.test.js.map |
{ | ||
"name": "async-source", | ||
"version": "1.1.9", | ||
"version": "2.0.0", | ||
"description": "async requests wrapper", | ||
@@ -45,2 +45,3 @@ "main": "dist/index.min.js", | ||
"eslint": "^8.39.0", | ||
"jsdom": "^25.0.1", | ||
"uglify-js": "3.14.3", | ||
@@ -47,0 +48,0 @@ "vitest": "^0.30.1" |
178
README.md
@@ -1,12 +0,21 @@ | ||
# AsyncSource # | ||
With AsyncSource you can | ||
* Create stateful reactive data source for asynchronous requests | ||
* Handle errors | ||
* Configure throttling | ||
* Ignore inconsistent state | ||
* Control state of your requests (isLoading, isFetch, data) | ||
# AsyncSource | ||
In case of multiple calls - only last call will be processed. | ||
Connect AsyncSource to your dynamic selects with in build debounce | ||
`AsyncSource` is a stateful, reactive data source designed for managing asynchronous requests in modern JavaScript and TypeScript projects. It provides robust features for error handling, throttling, and caching, making it ideal for applications that require efficient data management. | ||
### Features | ||
**Stateful Management:** Track the state of your requests, including (`isLoading`, `isFetched`, `data`). | ||
**Error Handling:** Automatically manage errors with customizable handlers, ensuring a smooth user experience. | ||
**Debounce Control:** Integrate built-in debouncing to manage rapid requests, preventing redundant calls and ensuring only the latest call is processed. | ||
**Cache Support:** Cache responses locally with configurable storage options and expiration times, enhancing performance and reducing unnecessary network requests. | ||
**Automatic State Consistency:** In cases of multiple calls, only the last call will be processed, maintaining data integrity and consistency. | ||
### Use Cases | ||
- Connect `AsyncSource` to dynamic selects to efficiently handle user interactions and data fetching. | ||
- Implement throttling and debouncing in applications that require real-time data updates without overwhelming the server. | ||
### Install ### | ||
@@ -145,1 +154,152 @@ ``` | ||
``` | ||
### Cache ### | ||
#### Global cache configuration #### | ||
##### To apply global settings, use setConfig: ##### | ||
```js | ||
import AsyncSource from 'async-source'; | ||
AsyncSource.setConfig({ | ||
cacheStorage: localStorage, // or any custom sync/async storage | ||
cacheTime: 12 * 60 * 60 * 1000, // cache for 12 hours | ||
cachePrefix: 'MyAppAsyncSource' // optional prefix for cache keys | ||
}); | ||
``` | ||
#### Instance-Level Configuration #### | ||
##### Specify custom settings for each AsyncSource instance by passing an options object as the third parameter. This object can include both caching and debounce settings. ##### | ||
```typescript | ||
new AsyncSource<T>( | ||
serviceMethod: (...args: any[]) => Promise<T>, | ||
errorHandler?: (error: any) => void, | ||
configOrDelay?: number | ConfigOptions | ||
) | ||
``` | ||
| Parameter | Required | Description | | ||
| --- | --- | --- | | ||
| `serviceMethod` | Yes | Method that returns a promise. | | ||
| `errorHandler` | No | Function to handle errors in the service method. | | ||
| `configOrDelay` | No | Debounce delay (in ms) or an object with config options. | | ||
#### ConfigOptions Interface #### | ||
##### If using a configuration object for configOrDelay, it can include: ##### | ||
```typescript | ||
interface ConfigOptions { | ||
debounceTime?: number; // Delay before request execution (in milliseconds) | ||
requestCacheKey?: string; // Request cache key (required for enable cache) | ||
cacheTime?: number; // Cache expiration in milliseconds | ||
cacheStorage?: CacheStorage; // Storage (e.g., localStorage, sessionStorage, indexDB) | ||
isUpdateCache?: boolean; // Refetch cache every time when true. | ||
} | ||
``` | ||
| Option | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| `debounceTime` | `number` | `300` | Delay before request execution (in milliseconds). | | ||
| `requestCacheKey` | `string` | | Request cache key (required for enabling cache). | | ||
| `cacheTime` | `number` | `43200000` | Cache expiration time in milliseconds (default: 12 hours). | | ||
| `cacheStorage` | `CacheStorage` | `localStorage` | Storage interface (e.g., localStorage, sessionStorage, indexedDB). | | ||
| `isUpdateCache` | `boolean` | `true` | Refetch cache every time when true. | | ||
#### Example #### | ||
#### Sync storage ### | ||
```vue | ||
<template> | ||
<ul v-loading="isLoading"> | ||
<li v-for="item in items" :key="item.id"> | ||
{{ item.name }} | ||
</li> | ||
</ul> | ||
</template> | ||
<script> | ||
import AsyncSource from 'async-source'; | ||
import { computed, reactive } from 'vue'; | ||
export default { | ||
name: 'MyComponent', | ||
setup() { | ||
function errorHandler(error) { | ||
console.error(error); | ||
} | ||
const source = reactive( | ||
new AsyncSource(request, errorHandler, { | ||
requestCacheKey: 'key', | ||
cacheStorage: sessionStorage, | ||
isUpdateCache: false, | ||
debounceTime: 100, | ||
cacheTime: 24 * 60 * 60 * 1000 // 24h | ||
}) | ||
); | ||
source.update(); | ||
const items = computed(() => source.data || []); | ||
const isLoading = computed(() => source.isLoading); | ||
return { | ||
items, | ||
isLoading | ||
}; | ||
} | ||
} | ||
</script> | ||
``` | ||
#### Async storage ### | ||
```vue | ||
<template> | ||
<ul v-loading="isLoading"> | ||
<li v-for="item in items" :key="item.id"> | ||
{{ item.name }} | ||
</li> | ||
</ul> | ||
</template> | ||
<script> | ||
import AsyncSource from 'async-source'; | ||
import { computed, reactive } from 'vue'; | ||
import { get, set, del } from 'idb-keyval'; | ||
export default { | ||
name: 'MyComponent', | ||
setup() { | ||
function errorHandler(error) { | ||
console.error(error); | ||
} | ||
const storage = { | ||
getItem: (key: string) => get(key), | ||
setItem: (key: string, value: string) => set(key, value), | ||
removeItem: (key: string) => del(key) | ||
}; | ||
const source = reactive( | ||
new AsyncSource(request, errorHandler, { | ||
requestCacheKey: 'key', | ||
cacheStorage: storage, | ||
isUpdateCache: false, | ||
debounceTime: 100, | ||
cacheTime: 24 * 60 * 60 * 1000 // 24h | ||
}) | ||
); | ||
source.update(); | ||
const items = computed(() => source.data || []); | ||
const isLoading = computed(() => source.isLoading); | ||
return { | ||
items, | ||
isLoading | ||
}; | ||
} | ||
} | ||
</script> | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
121419
1209
304
7