vuex-cache
Version:
Cache dispatched actions and prevent repeated requests and heavy actions.
377 lines (308 loc) • 9.83 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
/**
* Check if value is an object.
* @param {any} value
* @returns {value is Object}
*/
var isObject = function (value) {
return !!value && typeof value === 'object';
};
/**
* Type alias for Store or ActionContext instances.
* @typedef {import('vuex').Store<any> | import('vuex').ActionContext<any, any>} Store
*/
/**
* Convert value to `string`.
* @param {any} value
* @returns {string}
*/
var toString = function (value) {
return isObject(value) ? JSON.stringify(value) : String(value);
};
/**
* Dispatch's options object.
* @typedef {import('vuex').DispatchOptions & { timeout: number }} DispatchOptions
*/
/**
* Dispatch's payload object.
* @typedef {import('vuex').Payload & { timeout: number }} Payload
*/
/**
* Type alias for Dispatch parameters.
* @typedef {[string, any?, DispatchOptions?]|[Payload, DispatchOptions?]} DispatchParams
*/
/**
* Resolve Dispatch parameters.
* @param {DispatchParams} params
* @returns {[string, Payload?, DispatchOptions?]}
*/
var resolveParams = function (params) {
return isObject(params[0]) ? [params[0].type, params[0], params[1]] : params;
};
var GenerateKeyError = new Error("Can't generate key from parameters.");
/**
* Generate key from Dispatch parameters.
* @param {DispatchParams} params
* @returns {string|Error}
*/
var generateKey = function (params) {
try {
var ref = resolveParams(params);
var type = ref[0];
var payload = ref[1];
return (type + ":" + (toString(payload)));
} catch (_) {
return GenerateKeyError;
}
};
/**
* Check if value has timeout property.
* @param {any} value
* @returns {value is { timeout: number }}
*/
var hasTimeout = function (value) {
return isObject(value) && typeof value.timeout === 'number';
};
/**
* Type alias for options object.
* @typedef {{ timeout?: number }} Options
*/
/**
* Resolve timeout from parameters and plugin options.
* @param {DispatchParams} params
* @param {Options} [pluginOptions]
* @returns {number}
*/
var resolveTimeout = function (params, pluginOptions) {
var dispatchOptions = typeof params[0] === 'string' ? params[2] : params[0];
if (hasTimeout(dispatchOptions)) {
return dispatchOptions.timeout;
} else if (hasTimeout(pluginOptions)) {
return pluginOptions.timeout;
}
return 0;
};
/**
* Check if value (time) is expired.
* @param {number} [expiresIn]
* @returns {boolean}
*/
var isExpired = function (expiresIn) {
return !!expiresIn && Date.now() > expiresIn;
};
/**
* Cache's state record.
* @typedef {{ expiresIn?: number, value: Promise<any> }} CacheRecord
*/
/**
* Cache's state.
* @type {Map<string, CacheRecord>}
*/
var state = new Map();
/**
* Define cache property to store, or action context, object.
* @param {Store} store
* @param {Options} [options]
*/
var defineCache = function (store, options) {
var cache = {
/**
* Dispatch an action and set it on cache.
* @param {...DispatchParams} params
* @returns {Promise<any>}
*/
dispatch: function dispatch() {
var params = [], len = arguments.length;
while ( len-- ) params[ len ] = arguments[ len ];
var key = generateKey(params);
if (key === GenerateKeyError) {
// Fallback on generateKey errors.
return store.dispatch.apply(store, params);
}
var ref = state.get(key) || {};
var value = ref.value;
var expiresIn = ref.expiresIn;
if (!!value && !isExpired(expiresIn)) {
return value;
}
var timeout = resolveTimeout(params, options);
var record = {
expiresIn: timeout ? Date.now() + timeout : undefined,
value: store.dispatch.apply(store, params)
};
state.set(key, record);
return record.value.catch(function (error) {
state.delete(key);
return Promise.reject(error);
});
},
/**
* Check if an action dispatch is on cache.
* @param {...DispatchParams} params
* @returns {boolean}
*/
has: function has() {
var params = [], len = arguments.length;
while ( len-- ) params[ len ] = arguments[ len ];
var key = generateKey(params);
if (key === GenerateKeyError) {
// Fallback on generateKey errors.
return false;
}
var record = state.get(key);
return isObject(record) && !isExpired(record.expiresIn);
},
/**
* Clear cache. Returns `true` if cache was cleared and `false` otherwise.
* If using the type parameter, only actions with the specified type are
* deleted from cache and the number of deleted keys is returned.
* @returns {boolean|number}
*/
clear: function clear() {
var params = [], len = arguments.length;
while ( len-- ) params[ len ] = arguments[ len ];
var ref = resolveParams(params);
var type = ref[0];
if (type) {
return Array.from(state.keys()).filter(function (key) { return key.split(':')[0] === type; }).reduce(function (count, key) { return count + state.delete(key); }, 0);
}
return !!state.clear();
},
/**
* Delete an action dispatch from cache. Returns `true` if it was deleted
* and `false` otherwise.
* @returns {boolean}
*/
delete: function delete$1() {
var params = [], len = arguments.length;
while ( len-- ) params[ len ] = arguments[ len ];
var key = generateKey(params);
if (key === GenerateKeyError) {
// Fallback on generateKey errors.
return false;
}
return state.delete(key);
},
state: function state$1() {
return state;
}
};
Object.defineProperty(store, 'cache', {
value: cache,
writable: false,
enumerable: true,
configurable: false
});
for (var namespace in store._modulesNamespaceMap) {
var module = getModuleByNamespace(store, 'mapCacheActions', namespace);
Object.defineProperty(module.context, 'cache', {
value: cache,
writable: false,
enumerable: true,
configurable: false
});
}
};
/**
* Normalize the map
* normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
* normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]
* @param {Array|Object} map
* @return {Object}
*/
var normalizeMap = function (map) {
return Array.isArray(map) ? map.map(function (key) { return ({
key: key,
val: key
}); }) : Object.keys(map).map(function (key) { return ({
key: key,
val: map[key]
}); });
};
/**
* Search a special module from store by namespace. if module not exist, print error message.
* @param {Object} store
* @param {String} helper
* @param {String} namespace
* @return {Object}
*/
var getModuleByNamespace = function (store, helper, namespace) {
var module = store._modulesNamespaceMap[namespace];
if (process.env.NODE_ENV !== 'production' && !module) {
console.error(("[vuex-cache] module namespace not found in " + helper + "(): " + namespace));
}
return module;
};
/**
* Return a function expect two param contains namespace and map. it will normalize the namespace and then the param's function will handle the new namespace and the map.
* @param {Function} fn
* @return {Function}
*/
var normalizeNamespace = function (fn) {
return function (namespace, map) {
if (typeof namespace !== 'string') {
map = namespace;
namespace = '';
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/';
}
return fn(namespace, map);
};
};
/**
* Type alias for Action.
* @typedef {import('vuex').Action<any, any>} Action
*/
/**
* Create cache with options and define it on action context instance.
* @param {Action} action
* @param {Options} [options]
* @returns {Action}
*/
var cacheAction = function (action, options) { return function (context, payload) {
defineCache(context, options);
return action.call(this, context, payload);
}; };
/**
* Create cache actions object to map to a component
* @param {String} namespace
* @param {Array} actions
* @returns {Object}
*/
var mapCacheActions = normalizeNamespace(function (namespace, actions) {
var res = {};
normalizeMap(actions).forEach(function (ref) {
var key = ref.key;
var val = ref.val;
res[key] = function mappedAction() {
var this$1$1 = this;
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
var dispatch = this.$store.cache.dispatch;
if (namespace) {
var module = getModuleByNamespace(this.$store, 'mapCacheActions', namespace);
if (!module) {
return;
} // dispatch = module.context.cache.dispatch;
dispatch = typeof val === 'function' ? function (type) {
var ref;
var payload = [], len = arguments.length - 1;
while ( len-- > 0 ) payload[ len ] = arguments[ len + 1 ];
(ref = module.context.cache.dispatch).call.apply(ref, [ this$1$1.$store.cache, ("" + namespace + type) ].concat( payload ));
} : module.context.cache.dispatch;
}
return typeof val === 'function' ? val.call.apply(val, [ this, dispatch ].concat( args )) : dispatch.call.apply(dispatch, [ this.$store.cache, ("" + namespace + val) ].concat( args ));
};
});
return res;
});
/**
* Create cache with options and define it on store instance.
* @param {Options} options
* @returns {(store: Store) => void}
*/
var createCache = function (options) { return function (store) { return defineCache(store, options); }; };
exports.cacheAction = cacheAction;
exports["default"] = createCache;
exports.mapCacheActions = mapCacheActions;