{"id":16042066,"url":"https://github.com/nylen/resource-store","last_synced_at":"2026-06-19T19:32:14.974Z","repository":{"id":25099625,"uuid":"28520749","full_name":"nylen/resource-store","owner":"nylen","description":"Node.js caching/memoization library with multiple storage backends.","archived":false,"fork":false,"pushed_at":"2015-01-13T20:19:47.000Z","size":205,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-11T19:34:39.554Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nylen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-26T20:44:38.000Z","updated_at":"2020-02-23T03:02:58.000Z","dependencies_parsed_at":"2022-08-23T19:01:02.541Z","dependency_job_id":null,"html_url":"https://github.com/nylen/resource-store","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nylen/resource-store","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nylen%2Fresource-store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nylen%2Fresource-store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nylen%2Fresource-store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nylen%2Fresource-store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nylen","download_url":"https://codeload.github.com/nylen/resource-store/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nylen%2Fresource-store/sbom","scorecard":{"id":699890,"data":{"date":"2025-08-18","repo":{"name":"github.com/nylen/resource-store","commit":"398339af7cdfda7dddf131052ff094c923a30593"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":2.6,"checks":[{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T04:57:17.608Z","repository_id":25099625,"created_at":"2025-08-22T04:57:17.609Z","updated_at":"2025-08-22T04:57:17.609Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34546192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-09T00:01:21.916Z","updated_at":"2026-06-19T19:32:14.958Z","avatar_url":"https://github.com/nylen.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# resource-store\n\n`resource-store` is a Node.js caching/memoization library with multiple storage\nbackends.\n\n[![npm package](http://img.shields.io/npm/v/resource-store.svg?style=flat)](https://www.npmjs.org/package/resource-store)\n[![Build status](https://img.shields.io/travis/nylen/resource-store.svg?style=flat)](https://travis-ci.org/nylen/resource-store)\n[![Coverage](https://img.shields.io/coveralls/nylen/resource-store.svg?style=flat)](https://coveralls.io/r/nylen/resource-store)\n\n## Usage\n\nCreate a `ResourceStore` object with a generator callback.  Then, request\nvalues from it by calling the `get` method and passing JavaScript objects or\nprimitives as keys.  If the given key is not already cached, the generator\nfunction will be called to generate its value.\n\nThe generator function receives keys and generates the values associated with\nthem.  For a given key, the value it returns should always be the same.\n\nThe optional `backend` argument determines the type of storage used by the store:\n\n- **Omitted** - In-memory storage only, using `MemoryBackend`.\n- **String** - File-backed storage starting at the given directory, using\n  `FileBackend`.\n- **Object** - An object with methods `get`, `set`, `delete`, and `list`, see\n  [Backends](#backends) below for details.\n\nThe `extra` parameter passed to the generator function is an object that can be\nused to store extra data alongside the value generated from the key.  See\n[Out-of-Band Data](#out-of-band-data) below for more information.\n\n```js\nvar ResourceStore = require('resource-store');\n\nvar store = new ResoureStore(backend, function generator(key, extra, cb) {\n    var value;\n    // generate value based on key\n    // generator callback takes parameters (err, value)\n    cb(null, value);\n});\n```\n\n## Methods\n\n### get(key, cb)\n\nRetrieves a value from the store.  If the value for the given key has not been\ngenerated yet, the generator function will be called, otherwise the stored\nvalue will be returned.  If multiple calls are made to `get` for the given key,\nthen the generator function will only be called once and all outstanding calls\nwill return when the generator completes.\n\nThe callback to `get` takes parameters (`err`, `value`, `extra`):\n\n- `err` - `null` unless an error occurred in the generator function or the\n  storage backend.\n- `value` - the value associated with the requested key.\n- `extra` - see [Out-of-Band Data](#out-of-band-data) below.\n\n```js\nstore.get({ prop1 : 'value1' }, function(err, value, extra) {\n    // do stuff with the returned value\n});\n```\n\n### delete(key, [cb])\n\nDeletes a value from the store.  If the value specified by `key` does not exist\nthen an error will be returned.\n\n```js\nstore.delete({ prop1 : 'value1' }, function(err) {\n    // delete succeeded if err is null\n});\n```\n\n### list(cbEntry, cbDone)\n\nLists all values cached in the store.\n\n`cbEntry` is called once for each entry with parameters `err`, `key`, `value`,\n`extra`.  See [Out-of-Band Data](#out-of-band-data) below for more information\nabout the `extra` parameter.\n\n`cbDone` is called after all entries have been listed with parameters `err`,\n`numEntries`.\n\n```js\nstore.list(function cbEntry(err, key, value, extra) {\n    // do something with this value\n}, function cbDone(err, numEntries) {\n    // now go do something else\n});\n```\n\n## Out-of-Band Data\n\nSome callback functions receive an `extra` parameter which is the actual value\nsent to the storage backend (the value associated with the given key is stored\nat `extra.value`).  The generator function can set properties on `extra` to\nstore data alongside the cached value.  This can be useful for debugging.\n\nThe library will also set the following values on `extra` (all date/time values\nare millisecond-precision Unix timestamps, the result of `+new Date`):\n\n- `key` - the key object associated with this item.  Not very useful to\n  consumers, but needed because a backend may not know its own keys.\n- `value` - the data value associated with this key.\n- `createStarted` - available for all functions, but most useful in `get` and\n  `list`.\n- `createEnded` - set when the generator function completes; available for\n  `get` and `list`.\n- `lastRetrieved` - set when a value is generated or retrieved from the storage\n  backend.  Available in `get` and `list`, but most useful in `list` to allow\n  deleting old data.\n- `wasCached` - available in `get` and set to `true` if the requested value was\n  already cached (i.e. the generator function *was not* called).\n- `baseFilename` - available in all functions if using the `FileBackend` for\n  storage.  This is the data filename where the value will be stored, without\n  the `.json` extension.  This is useful for storing additional binary data\n  alongside the key.\n\n## Backends\n\nStorage backends that can be used by this library must store data objects\nassociated with string keys.  **Note**: `ResourceStore` accepts object keys,\nbut storage backends accept string keys.\n\nBackends must implement the following methods:\n\n- `get` - return the value associated with a key, and set and remember\n  `value.lastRetrieved`, or return `false` if nothing is stored with that key.\n- `set` - set the data value associated with a key.\n- `delete` - clear the data value associated with a key.\n- `list` - list all data values stored.\n\nBackends can have a `shouldCache` property.  If it is missing or truthy, then\n`ResourceStore` will remember values retrieved from the backend when multiple\ncalls to `get` are made concurrently for the same key.  Set\n`backend.shouldCache = false` to disable this behavior if retrieving data from\nthe backend is effectively instantaneous.\n\nPseudocode (to avoid\n[releasing Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony),\nall of these methods should be asynchronous in real implementations):\n\n```js\n// cb takes parameters (err, value)\nbackend.get = function(keyString, cb) {\n    // If the key doesn't have anything stored, return false\n    if (!(keyString in backend.data)) {\n        return cb(null, false);\n    }\n    // Get the data value associated with the key\n    var value = ...;\n    // Set lastRetrieved (and persist it somehow)\n    value.lastRetrieved = +new Date;\n    // err is null unless getting the value failed\n    cb(err, value);\n};\n\n// cb takes parameters (err)\nbackend.set = function(keyString, value, cb) {\n    // Set the data value associated with the key\n    backend.data[keyString] = value;\n    // err is null unless setting the value failed\n    cb(err);\n};\n\n// cb takes parameters (err)\nbackend.delete = function(keyString, cb) {\n    // Clear the data value associated with the key\n    // err is null unless deleting the value failed\n    cb(err);\n};\n\n// cbEntry takes parameters (err, key, value)\n// cbDone  takes parameters (err, numEntries)\nbackend.list = function(cbEntry, cbDone) {\n    // Get the list of keys stored\n    var keys = Object.keys(backend.data),\n        numEntries = 0;\n    // Loop and return values\n    keys.forEach(function(k) {\n        var value = backend.data[k];\n        // Skip values that were removed after obtaining the list of keys\n        if (typeof value != 'undefined') {\n            // Passing the key is optional:  some backends (like FileBackend)\n            // don't know their own keys, so pass null instead\n            // Set value.lastRetrieved if it isn't already set\n            value.lastRetrieved = backend.lastRetrieved[k];\n            cbEntry(null, k, value);\n            numEntries++;\n        }\n    });\n    cbDone(null, numEntries);\n};\n```\n\nExcept for the `list` method, backends do not need to worry about concurrency:\n`ResourceStore` will serialize operations for a given key so that only one of\n`get`, `set`, and `delete` is operating at a time.  The `list` method should\nstore a list of keys first, then loop over them and skip keys that no longer\nhave associated data values.\n\nThe library comes with two backends already implemented:\n`ResourceStore.FileBackend` and `ResourceStore.MemoryBackend`.\n\n`new FileBackend(dir)` stores data in JSON filenames named based on the hash of\nthe requested keys, starting at the directory `dir` and creating subdirectories\nas needed.\n\n`new MemoryBackend()` stores data in memory in a plain old JavaScript object.\n\n## Mutability\n\nWhen using the `MemoryBackend`, values stored in a `ResourceStore` are mutable\nby default.  This has the following consequences:\n\n- The value returned by the generator `===` the value returned to `get()` and\n  `list()` for the same key\n- Object prototype chains, function properties, etc. are preserved\n- The `ResourceStore` can be used to cache database connections or other\n  complex objects with internal state.\n\nIf you'd rather have an immutable data store instead, construct the\n`ResourceStore` using a `FileBackend`, or using an immutable `MemoryBackend` as\nfollows:\n\n```js\nvar store = new ResourceStore(\n    new ResourceStore.MemoryBackend({ mutable : false }),\n    function generator(key, extra, cb) { ... });\n```\n\nWith an immutable backend, the `ResourceStore` will only be able to store\nvalues that can be represented in JSON.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnylen%2Fresource-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnylen%2Fresource-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnylen%2Fresource-store/lists"}