Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

memoize-fs

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

memoize-fs - npm Package Compare versions

Comparing version 2.0.0 to 2.1.0

index.d.ts

3

CHANGELOG.md

@@ -0,1 +1,4 @@

### 2.1.0 (2020-02-28)
* feat: support custom serialize & deserialize through options
### 2.0.0 (2020-02-03)

@@ -2,0 +5,0 @@

108

index.js

@@ -1,7 +0,7 @@

const mkdirp = require('mkdirp')
const fs = require('fs')
'use strict'
const path = require('path')
const rmdir = require('rimraf')
const crypto = require('crypto')
const meriyah = require('meriyah')
const fs = require('fs-extra')

@@ -11,2 +11,7 @@ module.exports = buildMemoizer

const serializer = {
serialize,
deserialize
}
function serialize (val) {

@@ -30,15 +35,22 @@ const circRefColl = []

function deserialize (str) {
return JSON.parse(str).data
}
function getCacheFilePath (fn, args, opt) {
const salt = opt.salt || ''
const options = { ...serializer, ...opt }
const salt = options.salt || ''
let fnStr = ''
if (!opt.noBody) {
if (!options.noBody) {
fnStr = String(fn)
if (opt.astBody) {
if (options.astBody) {
fnStr = meriyah.parse(fnStr, { jsx: true, next: true })
}
fnStr = opt.astBody ? JSON.stringify(fnStr) : fnStr
fnStr = options.astBody ? JSON.stringify(fnStr) : fnStr
}
const argsStr = serialize(args)
const argsStr = options.serialize(args)
const hash = crypto.createHash('md5').update(fnStr + argsStr + salt).digest('hex')
return path.join(opt.cachePath, opt.cacheId, hash)
return path.join(options.cachePath, options.cacheId, hash)
}

@@ -50,3 +62,3 @@

// check args
if (typeof options !== 'object') {
if (!options || (options && typeof options !== 'object')) {
throw new Error('options of type object expected')

@@ -57,9 +69,37 @@ }

}
options = { ...serializer, ...options }
checkOptions(options)
function checkOptions (opts) {
if (opts.salt && typeof opts.salt !== 'string') {
throw new TypeError('salt option of type string expected, got: ' + typeof opts.salt)
}
if (opts.cacheId && typeof opts.cacheId !== 'string') {
throw new TypeError('cacheId option of type string expected, got: ' + typeof opts.cacheId)
}
if (opts.maxAge && typeof opts.maxAge !== 'number') {
throw new TypeError('maxAge option of type number bigger zero expected')
}
if (opts.serialize && typeof opts.serialize !== 'function') {
throw new TypeError('serialize option of type function expected')
}
if (opts.deserialize && typeof opts.deserialize !== 'function') {
throw new TypeError('deserialize option of type function expected')
}
}
// check for existing cache folder, if not found, create folder, then resolve
function initCache (cachePath) {
function initCache (cachePath, opts) {
return new Promise(function (resolve, reject) {
return mkdirp(cachePath).then(() => {
resolve()
}).catch(reject)
return fs.ensureDir(cachePath, { recursive: true })
.then(() => {
resolve()
})
.catch((err) => {
if (err && err.code === 'EEXIST' && opts.throwError === false) {
resolve()
return
}
reject(err)
})
})

@@ -69,14 +109,2 @@ }

function memoizeFn (fn, opt) {
function checkOptions (optExt) {
if (optExt.salt && typeof optExt.salt !== 'string') {
throw new Error('salt option of type string expected, got \'' + typeof optExt.salt + '\'')
}
if (optExt.cacheId && typeof optExt.cacheId !== 'string') {
throw new Error('cacheId option of type string expected, got \'' + typeof optExt.cacheId + '\'')
}
if (optExt.maxAge && typeof optExt.maxAge !== 'number') {
throw new Error('maxAge option of type number bigger zero expected')
}
}
if (opt && typeof opt !== 'object') {

@@ -86,11 +114,9 @@ throw new Error('opt of type object expected, got \'' + typeof opt + '\'')

const optExt = opt || {}
if (typeof fn !== 'function') {
throw new Error('fn of type function expected')
}
const optExt = { cacheId: './', ...options, ...opt }
checkOptions(optExt)
optExt.cacheId = optExt.cacheId || './'
function resolveWithMemFn () {

@@ -121,3 +147,3 @@ return new Promise(function (resolve) {

resultObj = { data: r }
resultString = serialize(resultObj)
resultString = optExt.serialize(resultObj)
} else {

@@ -163,3 +189,3 @@ resultString = '{"data":' + r + '}'

}
if (result && result.then && typeof result.then === 'function') {
if (result && typeof result.then === 'function') {
// result is a promise instance

@@ -199,3 +225,5 @@ return result.then(function (retObj) {

try {
return JSON.parse(resultString).data // will fail on NaN
const deserializedValue = optExt.deserialize(resultString)
return deserializedValue
} catch (e) {

@@ -240,3 +268,3 @@ return undefined

return initCache(path.join(options.cachePath, optExt.cacheId)).then(resolveWithMemFn)
return initCache(path.join(options.cachePath, optExt.cacheId), optExt).then(resolveWithMemFn)
}

@@ -250,9 +278,5 @@

const cachPath = cacheId ? path.join(options.cachePath, cacheId) : options.cachePath
rmdir(cachPath, function (err) {
if (err) {
reject(err)
} else {
resolve()
}
})
fs.remove(cachPath)
.then(resolve)
.catch(reject)
}

@@ -263,3 +287,3 @@ })

function getCacheFilePathBound (fn, args, opt) {
return getCacheFilePath(fn, args, Object.assign({}, opt, { cachePath: options.cachePath }))
return getCacheFilePath(fn, args, { ...options, ...opt, cachePath: options.cachePath })
}

@@ -266,0 +290,0 @@

{
"name": "memoize-fs",
"version": "2.0.0",
"description": "memoize/cache in file system solution for Node.js",
"version": "2.1.0",
"description": "Node.js solution for memoizing/caching a function and its return state into the file system",
"author": "Boris Diakur <contact@borisdiakur.com> (https://github.com/borisdiakur)",
"scripts": {
"test": "npm run eslint && mocha test",
"eslint": "eslint .",
"mocha": "mocha --reporter nyan test",
"istanbul": "rm -rf coverage && ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report html && open coverage/memoize-fs/index.js.html",
"coveralls": "rm -rf coverage && ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
"lint": "eslint .",
"pretest": "npm run lint",
"test": "nyc mocha",
"mocha": "mocha -R nyan",
"coverage": "nyc report --reporter text-lcov | coveralls",
"cov": "open-cli coverage/lcov-report/index.html"
},

@@ -22,9 +23,8 @@ "homepage": "https://github.com/borisdiakur/memoize-fs",

"engines": {
"node": ">= 10.0.0",
"node": ">= 10.13.0",
"npm": ">= 6.0.0"
},
"dependencies": {
"meriyah": "^1.9.7",
"mkdirp": "^1.0.3",
"rimraf": "^3.0.1"
"fs-extra": "^8.1.0",
"meriyah": "^1.9.7"
},

@@ -39,8 +39,14 @@ "devDependencies": {

"eslint-plugin-standard": "^4.0.1",
"istanbul": "^0.4.5",
"mocha": "^7.0.1",
"mocha-lcov-reporter": "1.3.0"
"mocha": "^7.1.0",
"nyc": "^15.0.0",
"open-cli": "^5.0.0",
"serialize-javascript": "^3.0.0"
},
"private": false,
"main": "index.js",
"typings": "index.d.ts",
"files": [
"index.js",
"index.d.ts"
],
"keywords": [

@@ -62,3 +68,16 @@ "memoize",

"license": "MIT",
"readmeFilename": "README.md"
"nyc": {
"exclude": [
"test"
],
"reporter": [
"text",
"lcov"
],
"check-coverage": true,
"branches": 80,
"lines": 80,
"functions": 80,
"statements": 80
}
}
# memoize-fs
memoize/cache in file system solution for Node.js
Node.js solution for memoizing/caching a function and its return state into the file system

@@ -35,21 +35,61 @@ [![Build Status](https://travis-ci.org/borisdiakur/memoize-fs.svg?branch=master)](https://travis-ci.org/borisdiakur/memoize-fs)

```javascript
var cachePath = require('path').join(__dirname, '..', 'cache'),
memoize = require('memoize-fs')({ cachePath: cachePath }),
fun = function (a, b) { return a + b; };
```js
const assert = require('assert')
const memoizeFs = require('memoize-fs')
memoize.fn(fun).then(function (memFn) {
memFn(1, 2).then(function (result) {
assert.strictEqual(result, 3);
return memFn(1, 2); // cache hit
}).then(function (result) {
assert.strictEqual(result, 3);
}).catch( /* handle error */ );
}).catch( /* handle error */ );
const memoizer = memoizeFs({ cachePath: './some-cache' })
console.log(memoizer)
// => {
// fn: [Function: fn],
// getCacheFilePath: [Function: getCacheFilePathBound],
// invalidate: [Function: invalidateCache]
// }
async function main () {
let idx = 0
const func = function foo (a, b) {
idx += a + b
return idx
}
const memoizedFn = await memoizer.fn(func)
const resultOne = await memoizedFn(1, 2)
assert.strictEqual(resultOne, 3)
assert.strictEqual(idx, 3)
const resultTwo = await memoizedFn(1, 2) // cache hit
assert.strictEqual(resultTwo, 3)
assert.strictEqual(idx, 3)
}
main().catch(console.error)
```
__Note that a result of a memoized function is always a [Promise](http://www.html5rocks.com/en/tutorials/es6/promises/) instance!__
_**NOTE:** that memoized function is always an async function and
the result of it is a Promise (if not `await`-ed as seen in above example)!_
### Memoizing asynchronous functions
- [Learn more about Promises](https://javascript.info/promise-basics)
- [Learn more about async/await](https://javascript.info/async-await)
### Signature
See [Types](#types) and [Options](#options) sections for more info.
```js
const memoizer = memoizeFs(MemoizeOptions)
console.log(memoizer)
// => {
// fn: [Function: fn],
// getCacheFilePath: [Function: getCacheFilePathBound],
// invalidate: [Function: invalidateCache]
// }
const memoizedFn = memoizer.fn(FunctionToMemoize, Options?)
```
## Memoizing asynchronous functions
memoize-fs assumes a function asynchronous if the last argument it accepts is of type `function` and that function itself accepts at least one argument.

@@ -59,3 +99,3 @@ So basically you don't have to do anything differently than when memoizing synchronous functions. Just make sure the above condition is fulfilled.

```javascript
```js
var funAsync = function (a, b, cb) {

@@ -76,3 +116,3 @@ setTimeout(function () {

### Memoizing promisified functions
## Memoizing promisified functions

@@ -85,3 +125,3 @@ You can also memoize a promisified function. memoize-fs assumes a function promisified if its result is _thenable_

```javascript
```js
var funPromisified = function (a, b) {

@@ -103,12 +143,47 @@ return new Promise(function (resolve, reject) {

### Options
## Types
```ts
export interface Options {
cacheId?: string;
salt?: string;
maxAge?: number;
force?: boolean;
astBody?: boolean;
noBody?: boolean;
serialize?: (val?: any) => string;
deserialize?: (val?: string) => any;
}
export type MemoizeOptions = Options & { cachePath: string };
export type FnToMemoize = (...args: any[]) => any;
export interface Memoizer {
fn: (fnToMemoize: FunctionToMemoize, options?: Options) => Promise<FunctionToMemoize>;
invalidate: (id?: string) => Promise<any>;
getCacheFilePath: (fnToMemoize: FunctionToMemoize, options: Options) => string;
}
declare function memoizeFs(options: MemoizeOptions): Memoizer;
export = memoizeFs;
```
## Options
When memoizing a function all below options can be applied in any combination.
The only required option is `cachePath`.
#### cacheId
### cachePath
Path to the location of the cache on the disk. This option is always **required**.
### cacheId
By default all cache files are saved into the __root cache__ which is the folder specified by the cachePath option:
```javascript
var memoize = require('memoize-fs')({ cachePath: require('path').join(__dirname, '../../cache' });
```js
var path = require('path')
var memoizer = require('memoize-fs')({ cachePath: path.join(__dirname, '../../cache') })
```

@@ -119,7 +194,7 @@

```javascript
memoize.fn(fun, { cacheId: 'foobar' }).then(...
```js
memoizer.fn(fnToMemoize, { cacheId: 'foobar' })
```
#### salt
### salt

@@ -131,44 +206,74 @@ Functions may have references to variables outside their own scope. As a consequence two functions which look exactly the same

```javascript
memoize.fn(fun, { salt: 'foobar' }).then(...
```js
memoizer.fn(fnToMemoize, { salt: 'foobar' })
```
#### maxAge
### maxAge
With `maxAge` option you can ensure that cache for given call is cleared after a predefined period of time (in milliseconds).
```javascript
memoize.fn(fun, { maxAge: 10000 }).then(...
```js
memoizer.fn(fnToMemoize, { maxAge: 10000 })
```
#### force
### force
The `force` option forces the re-execution of an already memoized function and the re-caching of its outcome:
```javascript
memoize.fn(fun, { force: true }).then(...
```js
memoizer.fn(fnToMemoize, { force: true })
```
#### astBody
### astBody
If you want to use the function AST instead the function body when generating the hash ([see serialization](#serialization)), set the option `astBody` to `true`. This allows the function source code to be reformatted without busting the cache. See https://github.com/borisdiakur/memoize-fs/issues/6 for details.
```javascript
memoize.fn(fun, { astBody: true }).then(...
```js
memoizer.fn(fnToMemoize, { astBody: true })
```
#### noBody
### noBody
If for some reason you want to omit the function body when generating the hash ([see serialization](#serialization)), set the option `noBody` to `true`.
```javascript
memoize.fn(fun, { noBody: true }).then(...
```js
memoizer.fn(fnToMemoize, { noBody: true })
```
### Manual cache invalidation
### serialize and deserialize
These two options allows you to control how the serialization and deserialization process works.
By default we use basic `JSON.stringify` and `JSON.parse`, but you may need more advanced stuff.
In the following example we are using [Yahoo's `serialize-javascript`](https://ghub.now.sh/serialize-javascript)
to be able to cache properly the return result of memoized function containing a `function`.
```js
const memoizeFs = require('memoize-fs')
const serialize = require('serialize-javascript')
const deserialize = (serializedJsString) => eval(`(${serializedJsString})`)
const memoizer = memoizeFs({ cachePath: './cache', serialize, deserialize })
function someFn (a) {
const bar = 123
setTimeout(() => {}, a * 10)
return {
bar,
getBar() { return a + bar }
}
}
memoizer.fn(someFn)
```
## Manual cache invalidation
You can delete the root cache (all cache files inside the folder specified by the cachePath option):
```javascript
memoize.invalidate().then(...
```js
memoizer.invalidate().then(() => { console.log('cache cleared') })
```

@@ -178,4 +283,4 @@

```javascript
memoize.invalidate('foobar').then(...
```js
memoizer.invalidate('foobar').then(() => { console.log('cache for "foobar" cleared') })
```

@@ -185,2 +290,4 @@

See also the [`options.seriliaze` and `options.deserialize`](#serialize-and-deserialize).
memoize-fs uses JSON to serialize the results of a memoized function.

@@ -194,5 +301,5 @@ It also uses JSON, when it tries to serialize the arguments of the memoized function in order to create a hash

```js
var memoize = require('memoize-fs')({cachePath: '/'})
memoize.getCacheFilePath(function () {}, ['arg', 'arg'], {cacheId: 'foobar'})
// -> '/foobar/06f254...'
var memoizer = require('memoize-fs')({ cachePath: './' })
memoizer.getCacheFilePath(function () {}, ['arg', 'arg'], { cacheId: 'foobar' })
// -> './foobar/06f254...'
```

@@ -235,7 +342,9 @@

Lint with:
```shell
npm run jshint
npm run lint
```
Test with:
```shell

@@ -248,5 +357,5 @@ npm run mocha

```shell
npm run istanbul
npm run cov
```
Then please commit with a __detailed__ commit message.
Then please commit with a **detailed** commit message.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc