@swarmy/lru-cache
Advanced tools
Comparing version 3.0.0 to 3.1.1
@@ -1,1 +0,1 @@ | ||
module.exports=function(t){var r={};function u(e){if(r[e])return r[e].exports;var n=r[e]={i:e,l:!1,exports:{}};return t[e].call(n.exports,n,n.exports,u),n.l=!0,n.exports}return u.m=t,u.c=r,u.d=function(e,n,t){u.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(n,e){if(1&e&&(n=u(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var t=Object.create(null);if(u.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var r in n)u.d(t,r,function(e){return n[e]}.bind(null,r));return t},u.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(n,"a",n),n},u.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},u.p="",u(u.s=0)}([function(e,n,t){"use strict";t.r(n);var l=function(e,n){null===n.newest?(n.newest=e,n.oldest=e):((n.newest.next=e).prev=n.newest,n.newest=e)},i=function(e,n){e!==n.newest&&(null===(n.newest.next=e).prev?(e.prev=n.newest,e.next.prev=null,n.oldest=e.next):(e.prev.next=e.next,e.next.prev=e.prev,e.prev=n.newest),e.next=null,n.newest=e)},r=function(e,n,t){null===e.next?n.newest=e.prev:e.next.prev=e.prev,null===e.prev?n.oldest=e.next:e.prev.next=e.next,t.delete(e.key)},c=function(e,n,t){var r,u,o,a=[];if(null===e)return a;for(;t.size>e;)a.push((u=t,void 0,o=(r=n).oldest,u.delete(r.oldest.key),r.oldest=r.oldest.next,r.oldest.prev=null,{key:o.key,value:o.value}));return a};function f(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,n=this instanceof f?this:Object.create(f.prototype),u=e;0===u&&(u=null);var o=new Map,a={newest:null,oldest:null};return n.setMaxSize=function(e){return(u=e)<1&&(u=null),c(u,a,o)},n.getMaxSize=function(){return u},n.getSize=function(){return o.size},n.set=function(e,n){var t=o.get(e);if(void 0!==t)return t.value=n,i(t,a),null;t={key:e,value:n,next:null,prev:null},o.set(e,t),l(t,a);var r=c(u,a,o);return 1===r.length?r[0]:null},n.get=function(e){var n=o.get(e);return void 0===n?n:(i(n,a),n.value)},n.getWithoutLruChange=function(e){var n=o.get(e);return void 0===n?n:n.value},n.delete=function(e){var n=o.get(e);return void 0!==n&&(r(n,a,o),!0)},n.forEach=function(e){for(var n=a.oldest;null!==n;)e(n.value,n.key),n=n.next},n.map=function(e){for(var n=a.oldest,t=[];null!==n;)t.push(e(n.value,n.key)),n=n.next;return t},n.clear=function(){var e=n.map(function(e,n){return{key:n,value:e}});return o.clear(),a.newest=null,a.oldest=null,e},n}function y(e){return function(e){if(Array.isArray(e)){for(var n=0,t=new Array(e.length);n<e.length;n++)t[n]=e[n];return t}}(e)||function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function a(n){for(var e=1;e<arguments.length;e++){var t=null!=arguments[e]?arguments[e]:{},r=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(t).filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.forEach(function(e){s(n,e,t[e])})}return n}function s(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}t.d(n,"registerCacheChangedHandler",function(){return w}),t.d(n,"cacheTransaction",function(){return S}),t.d(n,"getCache",function(){return L}),t.d(n,"clearAllCaches",function(){return _});var v=500,u=0,d=new Map,h=new Map,p=new Set,o=function(t,e){null===e||Array.isArray(e)&&0===e.length?p.add(t):(Array.isArray(e)?e:[e]).forEach(function(e){var n=h.get(e);void 0===n&&(n=new Set,h.set(e,n)),n.add(t)})},g=function(n,e){null===e||Array.isArray(e)&&0===e.length?p.delete(n):(Array.isArray(e)?e:[e]).forEach(function(e){h.get(e).delete(n)})},w=function(e){var n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,t=u;u+=1;var r={changedHandler:e,valueTypes:n,isActive:!0,unregister:function(){d.delete(t),g(t,n)},activate:function(){r.isActive=!0,o(t,n)},deactivate:function(){r.isActive=!1,g(t,n)},isRegistered:function(){return d.has(t)}};return d.set(t,r),o(t,n),r},m=function(t){var e,n,r,u=(e=t.valueTypes,n="string"==typeof e?[e]:e,r=new Set,n.forEach(function(e){var n=h.get(e);void 0!==n&&n.forEach(function(e){r.add(e)})}),p.forEach(function(e){r.add(e)}),r),o=[],a=0;if(u.forEach(function(e){var n=d.get(e);try{n.changedHandler(t)}catch(e){o.push(e)}finally{a+=1}}),0<o.length){var l="handleTransactionChangeObject: "+String(o.length)+" of "+String(a)+" handlers threw an error: ";o.forEach(function(e){l+=e.message+", "});var i=new Error(l);throw i.errors=o,i}},b=null,A=0,x=0,S=function(e){if(null===b&&(b={valueTypes:new Set}),x+=1,"function"==typeof e.finally)e.finally(function(){if(0===(x-=1))try{m(b)}finally{b=null,A=0}});else try{e()}finally{if(0===(x-=1))try{m(b)}finally{b=null,A=0}}},E=function(n,e,t,r){var u=b,o=null!==u;null===u&&(u={valueTypes:new Set}),u.valueTypes.has(n)?u[n][t].push(a({},e,{order:A++})):(u.valueTypes.add(n),u[n]=s({},t,[a({},e,{order:A++})]),r.forEach(function(e){u[n][e]=[]})),o||m(u)},O=!0,j=function(e,n){O&&E(e,n,"clearRemoves",["inserts","lruRemoves","deleteRemoves"])},k=function(e,n){O&&E(e,n,"lruRemoves",["clearRemoves","inserts","deleteRemoves"])},M=function(e,n){O&&E(e,n,"deleteRemoves",["clearRemoves","lruRemoves","inserts"])},R=function(u){return function(){for(var e=arguments.length,r=new Array(e),n=0;n<e;n++)r[n]=arguments[n];return new Promise(function(n,t){setTimeout(function(){try{var e=u.apply(void 0,r);n(e)}catch(e){t(e)}},0)})}},z=function(e,n){if(function(e){if(0<p.size)return!0;var n=h.get(e);return void 0!==n&&0<n.size}(e))S(n);else try{O=!1,n()}finally{O=!0}},C=function(c,f,s,e,v){if(!Array.isArray(e))throw new Error("LruCache::setAll: keyValueAlternateKeysArray must be an array");z(c,function(){e.forEach(function(e){var n=e.key,t=e.value,r=e.alternateKeys,u=f.getWithoutLruChange(n),o=Array.isArray(r)?r:[];0===o.length&&"string"==typeof r&&(o=[r]),o.forEach(function(e){if(s.has(e)&&s.get(e)!==n)throw new Error("LruCache::setAll: alternate key '"+e+"' is given for key '"+n+"' and value type '"+c+"' but is already used for key '"+s.get(e)+"'")}),o=new Set(o),void 0===u?u={key:n,value:t,alternateKeys:o}:(u.value=t,u.alternateKeys=new Set(y(u.alternateKeys).concat(y(o))));var a,l,i=f.set(n,u);o.forEach(function(e){s.set(e,n)}),a=c,l=u,O&&E(a,l,"inserts",["clearRemoves","lruRemoves","deleteRemoves"]),null!==i&&(i.value.alternateKeys.forEach(function(e){s.delete(e)}),v&&k(c,i.value))})})},T=function(e,n,t){var r=t(e);return void 0===r&&n.has(e)&&(r=t(n.get(e))),r};function P(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:v,n=this instanceof P?this:Object.create(P.prototype),r=f(e),u=new Map,o=!1,a=!1;return n.dispatchLruRemoves=function(e){o=e},n.dispatchClearRemoves=function(e){a=e},n.setAll=function(e){C(t,r,u,e,o)},n.setAllAsync=R(n.setAll),n.set=function(e){n.setAll([e])},n.setAsync=R(n.set),n.get=function(e){var n=T(e,u,r.get);return void 0===n?n:n.value},n.getWithoutLruChange=function(e){var n=T(e,u,r.getWithoutLruChange);return void 0===n?n:n.value},n.delete=function(e){var n=T(e,u,r.getWithoutLruChange);return void 0===n?(z(t,function(){M(t,{key:e})}),!1):(r.delete(n.key),n.alternateKeys.forEach(function(e){u.delete(e)}),z(t,function(){M(t,{key:e})}),!0)},n.forEach=r.forEach,n.getEntries=function(){return r.map(function(e){return e})},n.clear=function(){var e=r.clear();u.clear(),a&&z(t,function(){e.forEach(function(e){j(t,e.value)})})},n.getSize=r.getSize,n.getValueType=function(){return t},n.getMaxSize=r.getMaxSize,n.setMaxSize=function(e){var n=r.setMaxSize(e);z(t,function(){n.forEach(function(e){e.value.alternateKeys.forEach(function(e){u.delete(e)}),o&&k(t,e.value)})})},n}var K=new Map,L=function(e){var n=K.get(e);return void 0===n&&(n=P(e),K.set(e,n)),n},_=function(){S(function(){K.forEach(function(e){e.clear()})})}}]); | ||
module.exports=function(t){var r={};function u(e){if(r[e])return r[e].exports;var n=r[e]={i:e,l:!1,exports:{}};return t[e].call(n.exports,n,n.exports,u),n.l=!0,n.exports}return u.m=t,u.c=r,u.d=function(e,n,t){u.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(n,e){if(1&e&&(n=u(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var t=Object.create(null);if(u.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var r in n)u.d(t,r,function(e){return n[e]}.bind(null,r));return t},u.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(n,"a",n),n},u.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},u.p="",u(u.s=0)}([function(e,n,t){"use strict";t.r(n);var l=function(e,n){null===n.newest?(n.newest=e,n.oldest=e):((n.newest.next=e).prev=n.newest,n.newest=e)},i=function(e,n){e!==n.newest&&(null===(n.newest.next=e).prev?(e.prev=n.newest,e.next.prev=null,n.oldest=e.next):(e.prev.next=e.next,e.next.prev=e.prev,e.prev=n.newest),e.next=null,n.newest=e)},r=function(e,n,t){null===e.next?n.newest=e.prev:e.next.prev=e.prev,null===e.prev?n.oldest=e.next:e.prev.next=e.next,t.delete(e.key)},c=function(e,n,t){var r,u,o,a=[];if(null===e)return a;for(;t.size>e;)a.push((u=t,void 0,o=(r=n).oldest,u.delete(r.oldest.key),r.oldest=r.oldest.next,r.oldest.prev=null,{key:o.key,value:o.value}));return a};function f(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,n=this instanceof f?this:Object.create(f.prototype),u=e;0===u&&(u=null);var o=new Map,a={newest:null,oldest:null};return n.setMaxSize=function(e){return(u=e)<1&&(u=null),c(u,a,o)},n.getMaxSize=function(){return u},n.getSize=function(){return o.size},n.set=function(e,n){var t=o.get(e);if(void 0!==t)return t.value=n,i(t,a),null;t={key:e,value:n,next:null,prev:null},o.set(e,t),l(t,a);var r=c(u,a,o);return 1===r.length?r[0]:null},n.get=function(e){var n=o.get(e);return void 0===n?n:(i(n,a),n.value)},n.getWithoutLruChange=function(e){var n=o.get(e);return void 0===n?n:n.value},n.has=function(e){return o.has(e)},n.delete=function(e){var n=o.get(e);return void 0!==n&&(r(n,a,o),!0)},n.forEach=function(e){for(var n=a.oldest;null!==n;)e(n.value,n.key),n=n.next},n.map=function(e){for(var n=a.oldest,t=[];null!==n;)t.push(e(n.value,n.key)),n=n.next;return t},n.clear=function(){var e=n.map(function(e,n){return{key:n,value:e}});return o.clear(),a.newest=null,a.oldest=null,e},n}function y(e){return function(e){if(Array.isArray(e)){for(var n=0,t=new Array(e.length);n<e.length;n++)t[n]=e[n];return t}}(e)||function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function a(n){for(var e=1;e<arguments.length;e++){var t=null!=arguments[e]?arguments[e]:{},r=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(t).filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.forEach(function(e){s(n,e,t[e])})}return n}function s(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}t.d(n,"registerCacheChangedHandler",function(){return w}),t.d(n,"cacheTransaction",function(){return E}),t.d(n,"getCache",function(){return K}),t.d(n,"clearAllCaches",function(){return L});var v=500,u=0,d=new Map,h=new Map,p=new Set,o=function(t,e){null===e||Array.isArray(e)&&0===e.length?p.add(t):(Array.isArray(e)?e:[e]).forEach(function(e){var n=h.get(e);void 0===n&&(n=new Set,h.set(e,n)),n.add(t)})},g=function(n,e){null===e||Array.isArray(e)&&0===e.length?p.delete(n):(Array.isArray(e)?e:[e]).forEach(function(e){h.get(e).delete(n)})},w=function(e){var n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,t=u;u+=1;var r={changedHandler:e,valueTypes:n,isActive:!0,unregister:function(){d.delete(t),g(t,n)},activate:function(){r.isActive=!0,o(t,n)},deactivate:function(){r.isActive=!1,g(t,n)},isRegistered:function(){return d.has(t)}};return d.set(t,r),o(t,n),r},m=function(t){var e,n,r,u=(e=t.valueTypes,n="string"==typeof e?[e]:e,r=new Set,n.forEach(function(e){var n=h.get(e);void 0!==n&&n.forEach(function(e){r.add(e)})}),p.forEach(function(e){r.add(e)}),r),o=[],a=0;if(u.forEach(function(e){var n=d.get(e);try{n.changedHandler(t)}catch(e){o.push(e)}finally{a+=1}}),0<o.length){var l="handleTransactionChangeObject: "+String(o.length)+" of "+String(a)+" handlers threw an error: ";o.forEach(function(e){l+=e.message+", "});var i=new Error(l);throw i.errors=o,i}},b=null,A=0,x=0,E=function(e){if(null===b&&(b={valueTypes:new Set}),x+=1,"function"==typeof e.finally)e.finally(function(){if(0===(x-=1))try{m(b)}finally{b=null,A=0}});else try{e()}finally{if(0===(x-=1))try{m(b)}finally{b=null,A=0}}},S=function(n,e,t,r){var u=b,o=null!==u;null===u&&(u={valueTypes:new Set}),u.valueTypes.has(n)?u[n][t].push(a({},e,{order:A++})):(u.valueTypes.add(n),u[n]=s({},t,[a({},e,{order:A++})]),r.forEach(function(e){u[n][e]=[]})),o||m(u)},O=!0,j=function(e,n){O&&S(e,n,"clearRemoves",["inserts","lruRemoves","deleteRemoves"])},k=function(e,n){O&&S(e,n,"lruRemoves",["clearRemoves","inserts","deleteRemoves"])},M=function(e,n){O&&S(e,n,"deleteRemoves",["clearRemoves","lruRemoves","inserts"])},R=function(u){return function(){for(var e=arguments.length,r=new Array(e),n=0;n<e;n++)r[n]=arguments[n];return new Promise(function(n,t){setTimeout(function(){try{var e=u.apply(void 0,r);n(e)}catch(e){t(e)}},0)})}},z=function(e,n){if(function(e){if(0<p.size)return!0;var n=h.get(e);return void 0!==n&&0<n.size}(e))E(n);else try{O=!1,n()}finally{O=!0}},C=function(c,f,s,e,v){if(!Array.isArray(e))throw new Error("LruCache::setAll: keyValueAlternateKeysArray must be an array");z(c,function(){e.forEach(function(e){var n=e.key,t=e.value,r=e.alternateKeys,u=f.getWithoutLruChange(n),o=Array.isArray(r)?r:[];0===o.length&&"string"==typeof r&&(o=[r]),o.forEach(function(e){if(s.has(e)&&s.get(e)!==n)throw new Error("LruCache::setAll: alternate key '"+e+"' is given for key '"+n+"' and value type '"+c+"' but is already used for key '"+s.get(e)+"'")}),o=new Set(o),void 0===u?u={key:n,value:t,alternateKeys:o}:(u.value=t,u.alternateKeys=new Set(y(u.alternateKeys).concat(y(o))));var a,l,i=f.set(n,u);o.forEach(function(e){s.set(e,n)}),a=c,l=u,O&&S(a,l,"inserts",["clearRemoves","lruRemoves","deleteRemoves"]),null!==i&&(i.value.alternateKeys.forEach(function(e){s.delete(e)}),v&&k(c,i.value))})})};function T(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:v,a=this instanceof T?this:Object.create(T.prototype),r=f(e),l=new Map,u=!1,n=!1;a.dispatchLruRemoves=function(e){u=e},a.dispatchClearRemoves=function(e){n=e},a.setAll=function(e){C(t,r,l,e,u)},a.setAllAsync=R(a.setAll),a.set=function(e){a.setAll([e])},a.setAsync=R(a.set);var i=null,c=new Map,o=function(n,e){var t=2<arguments.length&&void 0!==arguments[2]&&arguments[2],r=e(n);if(void 0===r&&l.has(n))r=e(l.get(n));else if(void 0===r){if(t&&null!==i){if(c.has(n))return c.get(n);var u=i(n);if(!u)return r;if("function"!=typeof u.then)return a.set(u),u.value;var o=u.then(function(e){return e?(c.has(n)&&a.set(e),e.value):e}).finally(function(){c.delete(n)});return c.set(n,o),o}return r}return r.value};return a.setEntryGetter=function(e){i=e},a.get=function(e){return o(e,r.get,!0)},a.getWithoutLruChange=function(e){return o(e,r.getWithoutLruChange,!0)},a.has=function(e){return!!r.has(e)||l.has(e)},a.delete=function(e){var n=r.getWithoutLruChange(e);return void 0===n?(z(t,function(){M(t,{key:e})}),!1):(r.delete(n.key),n.alternateKeys.forEach(function(e){l.delete(e)}),z(t,function(){M(t,{key:e})}),!0)},a.forEach=r.forEach,a.getEntries=function(){return r.map(function(e){return e})},a.clear=function(){var e=r.clear();l.clear(),n&&z(t,function(){e.forEach(function(e){j(t,e.value)})})},a.getSize=r.getSize,a.getValueType=function(){return t},a.getMaxSize=r.getMaxSize,a.setMaxSize=function(e){var n=r.setMaxSize(e);z(t,function(){n.forEach(function(e){e.value.alternateKeys.forEach(function(e){l.delete(e)}),u&&k(t,e.value)})})},a}var P=new Map,K=function(e){var n=P.get(e);return void 0===n&&(n=T(e),P.set(e,n)),n},L=function(){E(function(){P.forEach(function(e){e.clear()})})}}]); |
{ | ||
"name": "@swarmy/lru-cache", | ||
"version": "3.0.0", | ||
"version": "3.1.1", | ||
"description": "LRU cache with event registry to handle cache changes", | ||
@@ -5,0 +5,0 @@ "author": "Gerd Neudert", |
@@ -5,2 +5,3 @@ # lru-cache | ||
* Alternate keys | ||
* Value getter for cache fails | ||
* Singleton caches per value type | ||
@@ -11,13 +12,28 @@ * Event registry for cache change events | ||
## Table of Contents | ||
1. [Installation](#section-installation) | ||
2. [Motivation](#section-motivation) | ||
3. [Basic Usage](#section-basic-usage) | ||
4. [Quality](#section-quality) | ||
5. [Develop](#section-develop) | ||
6. [Detailed Usage](#section-detailed-usage) | ||
7. [Performance](#section-performance) | ||
8. [Questions](#section-questions) | ||
9. [Roadmap](#section-roadmap) | ||
1. [What's New](#section-news) | ||
2. [Installation](#section-installation) | ||
3. [Motivation](#section-motivation) | ||
4. [Basic Usage](#section-basic-usage) | ||
5. [Quality](#section-quality) | ||
6. [Develop](#section-develop) | ||
7. [Detailed Usage](#section-detailed-usage) | ||
8. [Performance](#section-performance) | ||
9. [Questions](#section-questions) | ||
10. [Roadmap](#section-roadmap) | ||
## What's New <a name="section-news"></a> | ||
### Version 3.1.0 | ||
* New LruCache method `setEntryGetter` | ||
* Use it to set a function that provides a key-value-alternateKeys object (as expected by `set`) | ||
* If in a call to `get` or `getWithoutLruChange`, the given key is not in the cache and an entryGetter is set, then the entry getter will be called | ||
* In case of a synchronous entry getter, the returned entry will be inserted into the cache and the value is returned to the caller of the get method | ||
* In case of an asynchronous entry getter, a Promise will be returned to the caller of the get method | ||
* Subsequent calls to `get` or `getWithoutLruChange` will return the same Promise, until it is resolved | ||
* The Promise resolves to the `entry.value` returned by the entry getter | ||
* After the Promise resolved, the entry will be in the cache | ||
* New LruCache method `has` | ||
* To test if a key or alternate key is in the cache | ||
* Prior to 3.1.0, `getWithoutLruChange` could be used in all cases to test if a value is already cached. However, now the `has` method might be mandatory to do so in case an entry getter was set. | ||
## Installation <a name="section-installation"></a> | ||
@@ -58,3 +74,3 @@ ```javascript | ||
* [Detailed Usage](#caching-detail) | ||
* [JSDoc](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/docs/index.html) | ||
* [JSDoc](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/docs/index.html) | ||
@@ -72,8 +88,8 @@ ### Cache Events | ||
* [Detailed Usage](#cache-events-detail) | ||
* [JSDoc](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/docs/index.html) | ||
* [JSDoc](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/docs/index.html) | ||
## Quality <a name="section-quality"></a> | ||
* [Test results](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/test-report.html) | ||
* [Test coverage](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/coverage/index.html) | ||
* [Performance tests](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/performance-report.html) | ||
* [Test results](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/test-report.html) | ||
* [Test coverage](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/coverage/index.html) | ||
* [Performance tests](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/performance-report.html) | ||
@@ -109,7 +125,7 @@ ## Develop <a name="section-develop"></a> | ||
`clear` | none | undefined | Invalidate the cache. If dispatchClearRemoves is set true for the cache, then a single event will be dispatched for the clear. | ||
`delete` | keyOrAlternateKey: string | wasInCache: boolean | Remove an entry from the cache. A corresponding cache event will be dispatched. | ||
`delete` | key: string | wasInCache: boolean | Remove an entry from the cache. A corresponding cache event will be dispatched. Here, no alternate key is allowed! | ||
`dispatchClearRemoves` | newValue: boolean | undefined | Set whether a cache clear should dispatch a cache event (default: false). | ||
`dispatchLruRemoves` | newValue: boolean | undefined | Set whether a LRU-remove (entry being removed due to exceeded cache size) should dispatch a cache event (default: false). | ||
`forEach` | callback: function | undefined | Iterate over the cache from olodest to newest value. Callback receives cache entry as argument, being an object with `key`, `value` and `alternateKeys`. | ||
`get` | keyOrAlternateKey: string | value | get cached value or undefined. The returned value will be made the newest value in the cache. | ||
`get` | keyOrAlternateKey: string | value | Get cached value or undefined. The returned value will be made the newest value in the cache. If an entry getter is set (see `setEntryGetter`), this getter will be used in case of a cache miss. If the entry getter is an async function, then a Promise will be returned that resolves to the value (Subsequent calls to get will return the same Promise until resolved, so the entry getter is called only once). | ||
`getEntries` | none | Array | Returns an array with all cache entries, order from oldest to newest. | ||
@@ -120,7 +136,9 @@ `getMaxSize` | none | Int | Returns the current max size of the cache. | ||
`getWithoutLruChange` | keyOrAlternateKey: string | value | Like `get`, but without making the entry the newest in the cache. | ||
`has` | keyOrAlternateKey: string | isInCache: boolean | True, if the key or alternate key is in the cache. | ||
`set` | keyValueAlternateKeys: object | undefined | Insert or update a value in the cache. The argument must be an object with `key` and `value`. Optionally it can also have `alternateKeys`, being string or array of strings. After set, the value will be the newest in the cache. Dispatches a corresponding cache event. If an insert exceeds the max cache size and dispatchLruRemoves is set true, it will be included in the event. | ||
`setAll` | Array\[keyValueAlternateKeys\] | undefined | Like `set`, but for an array of cache entries. Leading to a single cache event. | ||
`setAllAsync` | Array\[keyValueAlternateKeys\] | Promise | Like `setAll`, but performing insert and event dispatch in another event loop, resolving the returned promise afterwards. | ||
`set` | keyValueAlternateKeys: object | Promise | Like `set`, but performing insert and event dispatch in another event loop, resolving the returned promise afterwards. | ||
`setMaxSize` | newMaxSize | undefined | Change the max size of the cache (default: 500). If the new maxSize is less than the old and this leads to LRU-removes and dispatchLruRemoves is set true, then a single cache event will be dispatched. | ||
`setAsync` | keyValueAlternateKeys: object | Promise | Like `set`, but performing insert and event dispatch in another event loop, resolving the returned promise afterwards. | ||
`setEntryGetter` | entryGetter: function | undefined | Set a getter that can be used to retrieve a cache entry (keyValueAlternateKeys-object) by key in case it is not yet in the cache. For values that might be called by alternate key, the getter should also be able to handle this. | ||
`setMaxSize` | newMaxSize: int | undefined | Change the max size of the cache (default: 500). If the new maxSize is less than the old and this leads to LRU-removes and dispatchLruRemoves is set true, then a single cache event will be dispatched. | ||
@@ -162,6 +180,6 @@ | ||
* Also compared to LRU maps that do not support alternate keys, there is a performance impact on get in case of cache misses. | ||
* See [Performance tests](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/performance-report.html) | ||
* See [Performance tests](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/performance-report.html) | ||
* However, get, set and delete are still O(1). (setMaxSize has O(size-newMaxSize), if size>newMaxSize) | ||
* Compared to a LRU cache without cache events, there is additional performance impact on get, set and delete. | ||
* Again see [Performance tests](https://rawcdn.githack.com/gneu77/lru-cache/6576f0a99e825cbc3736c9658036b0a6d224b027/performance-report.html) | ||
* Again see [Performance tests](https://rawcdn.githack.com/gneu77/lru-cache/0eca7d9be9c3901371f27ca08fd2c9c00adde925/performance-report.html) | ||
* However, if you are caching for performance, then because the fetching of values is significantly more time consuming. So whether you save 400ms or only 399ms hardly makes a difference here. | ||
@@ -183,3 +201,3 @@ * If you are not caching for performance reasons, but to have the change events, well than again it's just the price to pay for the event handling. | ||
### 3.0.x | ||
### 3.1.x | ||
* Fix issues that might occur | ||
@@ -190,6 +208,6 @@ * Improve README | ||
### 3.1.0 | ||
### 3.2.0 | ||
* Add option to use LruMap instead of LruCache (the LruMap is a currently not exported pure LRU cache, while the LruCache is a wrapper adding the event handling) | ||
### 3.2.0 | ||
### 3.3.0 | ||
* Make shape and content of change events configurable (with the current shape being used as default). |
@@ -324,10 +324,2 @@ import {LruMap} from "./LruMap"; | ||
const entryGetter = (key, alternateKeyToKey, getter) => { | ||
let entry = getter(key); | ||
if (typeof entry === "undefined" && alternateKeyToKey.has(key)) { | ||
entry = getter(alternateKeyToKey.get(key)); | ||
} | ||
return entry; | ||
}; | ||
/** Cannot be instantiated directly! Use 'getCache' to get a cache instance. | ||
@@ -410,4 +402,67 @@ * By default, cache events are dispatched only for inserts/updates and deletes. | ||
let entryGetter = null; | ||
const keyToPromise = new Map(); | ||
const internalGetter = (key, getter, useEntryGetter = false) => { | ||
let entry = getter(key); | ||
if (typeof entry === "undefined" && alternateKeyToKey.has(key)) { | ||
entry = getter(alternateKeyToKey.get(key)); | ||
} | ||
else if (typeof entry === "undefined") { | ||
if (useEntryGetter && entryGetter !== null) { | ||
if (keyToPromise.has(key)) { | ||
return keyToPromise.get(key); | ||
} | ||
const keyValueAlternateKeys = entryGetter(key); | ||
if (!keyValueAlternateKeys) { | ||
return entry; | ||
} | ||
if (typeof keyValueAlternateKeys.then === "function") { | ||
const promise = keyValueAlternateKeys.then(keyValueAlternateKeysResolved => { | ||
if (!keyValueAlternateKeysResolved) { | ||
return keyValueAlternateKeysResolved; | ||
} | ||
if (keyToPromise.has(key)) { | ||
// The condition is necessary, because meanwhile there might have been a self.delete(key) | ||
self.set(keyValueAlternateKeysResolved); | ||
} | ||
return keyValueAlternateKeysResolved.value; | ||
}).finally(() => { | ||
keyToPromise.delete(key); // important to use key and not keyValueAlternateKeysResolved.key, because key could also be an alternate key! | ||
}); | ||
keyToPromise.set(key, promise); | ||
return promise; | ||
} | ||
else { | ||
self.set(keyValueAlternateKeys); | ||
return keyValueAlternateKeys.value; | ||
} | ||
} | ||
else { | ||
return entry; | ||
} | ||
} | ||
return entry.value; | ||
}; | ||
/** Set a getter that can be used to retrieve a cache entry (keyValueAlternateKeys-object) by key in | ||
* case it is not yet in the cache. | ||
* For values that might be called by alternate key, the getter should also be able to handle this. | ||
* @memberof LruCache | ||
* @function | ||
* @param {function} newEntryGetter - function that takes a key as argument and returns corresponding entry or promise | ||
* @returns {undefined} void | ||
*/ | ||
self.setEntryGetter = newEntryGetter => { | ||
entryGetter = newEntryGetter; | ||
}; | ||
/** Get value from cache by either its key or one of its alternate keys (if exists). | ||
* Returns undefined, if not in cache. | ||
* If the value is not found in the cache and an entry-getter was set via setEntryGetter, then: | ||
* - If the entry getter returns a Promise, a Promise resolving to the value will be returned. | ||
* When the Promise resolves, the entry will be set to the cache. Until the Promise is not resolved, | ||
* subsequent calls to get will return the same Promise. | ||
* - If the entry getter returns a cache entry (keyValueAlternateKeys-object), this will be set to the cache | ||
* and the value will be returned. | ||
* - If the entry getter returns null or undefined, undefined will be returned. | ||
* If the key is not in the cache and no entry getter is set, undefined will be returned. | ||
* Makes the corresponding entry the most recently used (use 'getWithoutLruChange' to avoid this). | ||
@@ -417,18 +472,29 @@ * @memberof LruCache | ||
* @param {string} keyOrAlternateKey - The key or alternate key of the value | ||
* @returns {object | undefined} object, if the key is in cache, else undefined | ||
* @returns {value | Promise | undefined} value, promised value or undefined | ||
*/ | ||
self.get = keyOrAlternateKey => { | ||
const entry = entryGetter(keyOrAlternateKey, alternateKeyToKey, lruMap.get); | ||
return typeof entry === "undefined" ? entry : entry.value; | ||
}; | ||
self.get = keyOrAlternateKey => internalGetter(keyOrAlternateKey, lruMap.get, true); | ||
/** Like 'get', but not making the corresponding entry the most recently used. | ||
* If the value is retrieved via entry getter, it will of course still become the | ||
* most recently used. | ||
* @memberof LruCache | ||
* @function | ||
* @param {string} keyOrAlternateKey - The key or alternate key of the value | ||
* @returns {object | undefined} object, if the key is in cache, else undefined | ||
* @returns {value | Promise | undefined} value, promised value or undefined | ||
*/ | ||
self.getWithoutLruChange = keyOrAlternateKey => { | ||
const entry = entryGetter(keyOrAlternateKey, alternateKeyToKey, lruMap.getWithoutLruChange); | ||
return typeof entry === "undefined" ? entry : entry.value; | ||
self.getWithoutLruChange = keyOrAlternateKey => internalGetter(keyOrAlternateKey, lruMap.getWithoutLruChange, true); | ||
/** Return whether the cache contains an entry for the given key or alternate key | ||
* @memberof LruCache | ||
* @function | ||
* @param {string} keyOrAlternateKey - The entry key or alternate key | ||
* @return {boolean} true, if the given key or alternate key is in the cache | ||
*/ | ||
self.has = keyOrAlternateKey => { | ||
if (lruMap.has(keyOrAlternateKey)) { | ||
return true; | ||
} | ||
else { | ||
return alternateKeyToKey.has(keyOrAlternateKey); | ||
} | ||
}; | ||
@@ -445,3 +511,3 @@ | ||
self.delete = key => { | ||
const entry = entryGetter(key, alternateKeyToKey, lruMap.getWithoutLruChange); | ||
const entry = lruMap.getWithoutLruChange(key); | ||
if (typeof entry === "undefined") { | ||
@@ -448,0 +514,0 @@ wrapInTransaction(valueType, () => { |
@@ -169,2 +169,8 @@ | ||
/** Return whether the LruMap contains key | ||
* @param {string} key - The entry key | ||
* @return {boolean} true, if the given key is in the LruMap | ||
*/ | ||
self.has = key => keyToEntry.has(key); | ||
/** Remove the entry with the given key from the map. Returns false, in case the key was not in the map | ||
@@ -171,0 +177,0 @@ * @param {string} key - The entry key |
@@ -7,2 +7,3 @@ /* eslint-disable no-magic-number, max-lines */ | ||
registerCacheChangedHandler, | ||
clearAllCaches, | ||
} from "../LruCache"; | ||
@@ -12,2 +13,3 @@ | ||
const VALUE_TYPE_2 = "valueType2"; | ||
const VALUE_TYPE_3 = "valueType3"; | ||
const DEFAULT_CACHE_SIZE = 500; | ||
@@ -46,3 +48,3 @@ | ||
it("should allow to pass an array of alternate keys to 'set' method. 'get' must also work with the alternate key", () => { | ||
it("should allow to pass an array of alternate keys to 'set' method. 'get' and 'has' must also work with the alternate key", () => { | ||
const cache1 = getCache(VALUE_TYPE_1); | ||
@@ -58,2 +60,5 @@ | ||
expect(typeof cache1.get("myAltKey3")).toEqual("undefined"); | ||
expect(cache1.has("key1")).toBeTruthy(); | ||
expect(cache1.has("myAltKey2")).toBeTruthy(); | ||
expect(cache1.has("myAltKey3")).toBeFalsy(); | ||
@@ -216,3 +221,3 @@ cache1.clear(); | ||
it("should provide a 'delete' method that works with key or alternate key", () => { | ||
it("should provide a 'delete' method that works with key, but not with alternate key", () => { | ||
const cache1 = getCache(VALUE_TYPE_1); | ||
@@ -239,4 +244,4 @@ | ||
expect(deleteResult2).toBeFalsy(); | ||
expect(deleteResult3).toBeTruthy(); | ||
expect(deleteResult4).toBeFalsy(); | ||
expect(deleteResult3).toBeFalsy(); | ||
expect(deleteResult4).toBeTruthy(); | ||
expect(cache1.getSize()).toEqual(0); | ||
@@ -304,2 +309,76 @@ | ||
it("should provide a method to define an entry getter that is used in case of a cache miss", () => { | ||
const type3GetterSync = key => ({ | ||
key, | ||
value: key + "_value", | ||
alternateKeys: [key + "_alternate"], | ||
}); | ||
const cache = getCache(VALUE_TYPE_3); | ||
cache.setEntryGetter(type3GetterSync); | ||
cache.set({ | ||
key: "key1", | ||
value: "value1", | ||
}); | ||
expect(cache.get("key1")).toEqual("value1"); | ||
expect(cache.get("key2")).toEqual("key2_value"); | ||
expect(cache.get("key2_alternate")).toEqual("key2_value"); | ||
cache.setEntryGetter(null); | ||
cache.clear(); | ||
}); | ||
it("should provide a method to define an entry async getter that is used in case of a cache miss", async () => { | ||
let nCalls = 0; | ||
const type3GetterAsync = key => new Promise(resolve => { | ||
nCalls += 1; | ||
setTimeout(() => { | ||
resolve({ | ||
key, | ||
value: key + "_value", | ||
alternateKeys: [key + "_alternate"], | ||
}); | ||
}, 200) | ||
}); | ||
const cache = getCache(VALUE_TYPE_3); | ||
cache.setEntryGetter(type3GetterAsync); | ||
expect(typeof cache.get("key1") === "object").toBeTruthy(); | ||
expect(typeof cache.get("key1").then === "function").toBeTruthy(); | ||
const val = await cache.get("key1"); | ||
expect(val).toEqual("key1_value"); | ||
expect(cache.get("key1")).toEqual("key1_value"); | ||
// We must also ensure that the getter was called only once: | ||
expect(nCalls).toEqual(1); | ||
cache.setEntryGetter(null); | ||
cache.clear(); | ||
}); | ||
it("should handle the case where a getter returns undefined", async () => { | ||
let nCalls = 0; | ||
const type3GetterAsync = () => new Promise(resolve => { | ||
nCalls += 1; | ||
setTimeout(() => { | ||
resolve(undefined); // eslint-disable-line no-undefined | ||
}, 200) | ||
}); | ||
const cache = getCache(VALUE_TYPE_3); | ||
cache.setEntryGetter(type3GetterAsync); | ||
expect(typeof cache.get("key1") === "object").toBeTruthy(); | ||
expect(typeof cache.get("key1").then === "function").toBeTruthy(); | ||
const val = await cache.get("key1"); | ||
expect(typeof val).toEqual("undefined"); | ||
expect(typeof cache.get("key1")).toEqual("object"); | ||
// We must also ensure that the getter was called only once: | ||
expect(nCalls).toEqual(2); | ||
cache.setEntryGetter(null); | ||
cache.clear(); | ||
}); | ||
}); | ||
@@ -334,2 +413,41 @@ | ||
describe("clearAllCaches", () => { | ||
it("should clear all caches", () => { | ||
const cache1 = getCache(VALUE_TYPE_1); | ||
const cache2 = getCache(VALUE_TYPE_2); | ||
cache1.setAll([ | ||
{ | ||
key: "key1", | ||
value: "value1 of type 1", | ||
alternateKeys: ["myAltKey1"], | ||
}, | ||
{ | ||
key: "key2", | ||
value: "value2 of type 1", | ||
alternateKeys: "myAltKey2", | ||
}, | ||
]); | ||
cache2.setAll([ | ||
{ | ||
key: "key1", | ||
value: "value1 of type 2", | ||
alternateKeys: ["myAltKey1"], | ||
}, | ||
{ | ||
key: "key2", | ||
value: "value2 of type 2", | ||
alternateKeys: "myAltKey2", | ||
}, | ||
]); | ||
expect(cache1.get("key1")).toEqual("value1 of type 1"); | ||
expect(cache2.get("key1")).toEqual("value1 of type 2"); | ||
clearAllCaches(); | ||
expect(typeof cache1.get("key1")).toEqual("undefined"); | ||
expect(typeof cache2.get("key1")).toEqual("undefined"); | ||
}); | ||
}); | ||
describe("registerCacheChangedHandler", () => { | ||
@@ -439,2 +557,31 @@ | ||
it("should throw an error in case of throwing change handlers, but still keep the cache consistent", () => { | ||
const cache1 = getCache(VALUE_TYPE_1); | ||
const handlerHandle = registerCacheChangedHandler(() => { | ||
throw new Error("this is a test"); | ||
}); | ||
expect(() => { | ||
cache1.setAll([ | ||
{ | ||
key: "key1", | ||
value: "value1 of type 1", | ||
alternateKeys: "myAltKey1", | ||
}, | ||
{ | ||
key: "key2", | ||
value: "value2 of type 1", | ||
alternateKeys: "myAltKey2", | ||
}, | ||
]) | ||
}).toThrow("handleTransactionChangeObject: 1 of 1 handlers threw an error: this is a test, "); | ||
expect(cache1.get("key1")).toEqual("value1 of type 1"); | ||
expect(cache1.get("key2")).toEqual("value2 of type 1"); | ||
expect(cache1.get("myAltKey1")).toEqual("value1 of type 1"); | ||
handlerHandle.unregister(); | ||
cache1.clear(); | ||
}); | ||
it("should create correct change events for delete", () => { | ||
@@ -441,0 +588,0 @@ const cache1 = getCache(VALUE_TYPE_1); |
@@ -13,3 +13,3 @@ /* eslint-disable no-magic-number */ | ||
it("should provide set and get methods like a Map", () => { | ||
it("should provide set, get and has methods like a Map", () => { | ||
const lruMap = LruMap(); | ||
@@ -21,2 +21,4 @@ lruMap.set("key1", "value1"); | ||
expect(typeof lruMap.get("key2")).toEqual("undefined"); | ||
expect(lruMap.has("key1")).toBeTruthy(); | ||
expect(lruMap.has("key2")).toBeFalsy(); | ||
}); | ||
@@ -23,0 +25,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
104373
2246
205