UNPKG

vuex-cache

Version:

Cache dispatched actions and prevent repeated requests and heavy actions.

377 lines (308 loc) 9.83 kB
'use strict'; 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;