{"id":44524,"library":"caching-map","title":"caching-map","description":"caching-map v1.0.2 is an in-memory LRU cache with an ES6 Map-like API. It supports configurable cache limits, per-key cost for memory-aware eviction, per-key TTL expiration, and a materialize callback to avoid thundering herds for async resources. Unlike lru-cache, it offers easy enable/disable via zero/infinite limits and integrates with promises for async loading. Release cadence is low; no recent updates. Key differentiators include cost-based eviction, expired-key-first eviction, and full iteration order from most to least recently used.","status":"active","version":"1.0.2","language":"javascript","source_language":"en","source_url":"https://github.com/broadly/caching-map","tags":["javascript","cache","caching","es2015","es6","in-memory","lru","lru-cache","map"],"install":[{"cmd":"npm install caching-map","lang":"bash","label":"npm"},{"cmd":"yarn add caching-map","lang":"bash","label":"yarn"},{"cmd":"pnpm add caching-map","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"This package is CommonJS-only. ESM dynamic import works but is not the primary pattern.","wrong":"import Cache from 'caching-map';","symbol":"caching-map (default export)","correct":"const Cache = require('caching-map');"},{"note":"Methods are instance methods, not static.","wrong":"Cache.get('key')","symbol":"Cache (instance methods)","correct":"const cache = new Cache(100);\ncache.get('key');\ncache.set('key', value);\ncache.delete('key');\ncache.keys();"},{"note":"The materialize function is assigned directly to the cache instance, not passed in constructor options.","wrong":"new Cache(10, { materialize: fn })","symbol":"materialize callback","correct":"cache.materialize = async (key) => { return fetchData(key); };"}],"quickstart":{"code":"const Cache = require('caching-map');\nconst cache = new Cache(10);\n\ncache.materialize = async (key) => {\n  // Simulate async fetch with delay\n  return new Promise(resolve => setTimeout(() => resolve(`Value for ${key}`), 100));\n};\n\nasync function main() {\n  // First call triggers materialize\n  const val1 = await cache.get('a');\n  console.log(val1); // \"Value for a\"\n\n  // Second call returns cached value\n  const val2 = await cache.get('a');\n  console.log(val2); // \"Value for a\" (instant)\n\n  // Check cache stats\n  console.log(cache.size); // 1\n  console.log([...cache.keys()]); // ['a']\n  console.log('cost:', cache.cost); // 1 (default cost per key)\n\n  // Set with custom TTL (100ms) and cost\n  cache.set('b', 'short-lived', { ttl: 100, cost: 2 });\n  console.log(cache.size); // 2\n  await new Promise(r => setTimeout(r, 150));\n  console.log(cache.has('b')); // false (expired)\n  console.log(cache.size); // 1\n}\nmain();","lang":"javascript","description":"Shows basic usage: create cache, set materialize callback, get with async resolution, set with TTL and cost, iteration, and expiration."},"warnings":[{"fix":"Ensure materialize returns the desired value, or if it returns a Promise, cache.get will wait for it. For synchronous caches, do not return a Promise.","message":"materialize must be a function that returns a value (not a Promise) or a Promise. In previous versions, the return value was used as-is; if you return a Promise, cache.get returns that Promise, not its resolved value. (Current behavior: returns the resolved value if materialize returns a Promise, cache.get resolves it. But be consistent.)","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"To clear the cache when changing limit, call cache.clear() explicitly.","message":"Changing the cache limit at runtime does NOT automatically evict keys. Eviction only happens when a new key is set and the cache exceeds the limit. Setting limit to 0 does not clear the cache; subsequent gets will miss and trigger materialize.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Understand that 'limit' is a budget of total cost, not key count. Set appropriate costs consistent with your limit.","message":"The 'cost' option in set() is not the byte size but an arbitrary number. The default is 1, so limit acts as max key count. If you set cost > 1 for some keys, you may hit limit unexpectedly.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"If you need automatic cleanup, use lru-cache with 'ttlAutopurge' or implement periodic pruning.","message":"Expired keys are only evicted when a new key is added and the cache is over limit. They do NOT expire automatically in the background. An expired key can still exist in the cache and be returned by get()? Actually get() checks TTL and returns undefined if expired. But the entry is still present in internal storage until eviction or explicit delete.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"If copying, costs are copied from the source only if the source is a Cache; Map entries get default cost (1). TTL is not copied from Map entries; they will have no TTL.","message":"The constructor second argument can be a Map or another Cache to copy entries. This may cause unexpected behavior if the source has materialize callbacks or custom costs.","severity":"getting","affected_versions":">=1.0.0"}],"env_vars":null,"search_vec":"'api':21 'async':50,68 'avoid':46 'awar':34 'base':82 'cach':2,5,14,25,55,100,101,110 'caching-map':1,4 'cadenc':71 'callback':44 'configur':24 'cost':30,81 'cost-bas':80 'differenti':78 'easi':58 'enable/disable':59 'es2015':102 'es6':17,103 'evict':35,83,88 'expir':40,85 'expired-key-first':84 'first':87 'full':90 'herd':48 'in-memori':10,104 'includ':79 'integr':64 'iter':91 'javascript':99 'key':29,38,77,86 'least':96 'like':20 'limit':26,62 'load':69 'low':73 'lru':13,54,107,109 'lru-cach':53,108 'map':3,6,19,111 'map-lik':18 'materi':43 'memori':12,33,106 'memory-awar':32 'offer':57 'order':92 'per':28,37 'per-key':27,36 'promis':66 'recent':75,97 'releas':70 'resourc':51 'support':23 'thunder':47 'ttl':39 'unlik':52 'updat':76 'use':98 'v1.0.2':7 'via':60 'zero/infinite':61","created_at":"2026-06-07T12:50:33.524277+00:00","updated_at":"2026-06-07T12:50:33.524277+00:00","problems":[{"fix":"Assign cache.materialize = async (key) => { ... } before calling cache.get(key).","cause":"The materialize property was not assigned or was assigned after attempting to get a missing key.","error":"TypeError: cache.materialize is not a function"},{"fix":"Use new Cache(limit) and then assign properties like cache.materialize = fn.","cause":"Trying to pass materialize or other options via an object like new Cache({ limit: 10, materialize: fn }). The constructor only accepts (limit, [iterable]).","error":"Cache constructor does not accept an options object"},{"fix":"Ensure materialize does not call cache.get(key) for the same key it's being called for.","cause":"Recursive get inside materialize (materialize calls cache.get on the same key, causing infinite loop).","error":"Maximum call stack size exceeded"},{"fix":"const cache = new Cache(10); then cache.get(key);","cause":"Forgot to instantiate the Cache; used Cache.get instead of instance.get.","error":"Cannot read property 'get' of undefined"}],"ecosystem":"npm","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":null,"cli_name":null,"cli_version":null,"type":"library","homepage":"https://github.com/broadly/caching-map#readme","github":"https://github.com/broadly/caching-map","docs":null,"changelog":null,"pypi":null,"npm":"caching-map","openapi_spec":null,"status_page":null,"smithery":null,"categories":["storage"],"base_url":null,"auth_type":null,"provenance":{"verified_status":null,"verified_at":null,"last_verified":"2026-06-07","next_check":"2026-09-05","install_tag":null}}