Socket
Socket
Sign inDemoInstall

node-persist

Package Overview
Dependencies
Maintainers
3
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-persist - npm Package Compare versions

Comparing version 1.0.1 to 2.0.0

2

package.json
{
"name": "node-persist",
"version": "1.0.1",
"version": "2.0.0",
"description": "Super-easy (and fast) persistent data structures in Node.js, modeled after HTML5 localStorage",

@@ -5,0 +5,0 @@ "main": "./src/node-persist.js",

@@ -63,2 +63,9 @@ # node-persist

## 2.0.0 change logs
Non-backward changes
* filenames on the file system are now md5 hashed now and the structure of the saved data has changed to include the ttl in them.
* no longer need/support a `options.ttlDir`, since the `ttls` are now stored in the same file as each value
## 1.0.0 change logs

@@ -65,0 +72,0 @@

@@ -8,2 +8,3 @@ /*

path = require('path'),
crypto = require('crypto'),
mkdirp = require('mkdirp'),

@@ -24,3 +25,3 @@ Q = require('q'),

defaultTTL = 24 * 60 * 60 * 1000 /* ttl is truthy but not a number ? 24h default */,
defaultTTL = 24 * 60 * 60 * 1000 /* if ttl is truthy but it's not a number, use 24h as default */,

@@ -39,23 +40,6 @@ isNumber = function(n) {

btoa = function (string) {
return new Buffer(string.toString(), 'binary').toString('base64');
md5 = function (data) {
return crypto.createHash('md5').update(data).digest("hex");
},
atob = function (string) {
return new Buffer(string, 'base64').toString('binary');
},
sanitize = function (string) {
return btoa(string).replace(btoaPathSepRegExp, atobPathSepReplacement);
},
unsanitize = function (string) {
return atob(string.replace(atobPathSepReplacementRegExp, '/'));
},
btoaPathSepRegExp = new RegExp(path.sep.replace('\\', '\\\\'), 'g'),
atobPathSepReplacement = '__SLASH__',
atobPathSepReplacementRegExp = new RegExp(atobPathSepReplacement, 'g'),
/*

@@ -76,9 +60,8 @@ * To support backward compatible callbacks,

this.data = {};
this.ttls = {};
this.changes = {};
this.setOptions(userOptions);
// we don't call init in the constructor because we can only so for the initSync
// we don't call init in the constructor because we can only do so for the initSync
// for init async, it returns a promise, and in order to maintain that API, we cannot return the promise in the constructor
// so init must be called on the instance of new LocalStorage();
// so init must be called, separately, on the instance of new LocalStorage();
};

@@ -102,5 +85,3 @@

// dir is not absolute
options.dir = this.resolveDir(options.dir);
options.ttlDir = options.dir + '-ttl';
options.ttl = options.ttl ? isNumber(options.ttl) && options.ttl > 0 ? options.ttl : defaultTTL : false;

@@ -135,9 +116,4 @@ }

var result = {dir: options.dir};
deferreds.push(this.parseDataDir());
deferreds.push(this.parseStorageDir());
if (options.ttl) {
result.ttlDir = options.ttlDir;
deferreds.push(this.parseTTLDir());
}
//start persisting

@@ -170,11 +146,7 @@ if (options.interval && options.interval > 0) {

this.log("options:");
this.log(options.stringify(options));
this.log(this.stringify(options));
}
this.parseDataDirSync();
this.parseStorageDirSync();
if (options.ttl) {
this.parseTTLDirSync();
}
//start synchronous persisting,

@@ -196,3 +168,3 @@ if (options.interval && options.interval > 0) {

return this.keys().forEach(function(key) {
callback(key, this.data[key]);
callback(key, this.data[key].value);
}.bind(this));

@@ -203,7 +175,6 @@ },

return this.keys().map(function(k) {
return this.data[k];
return this.data[k].value;
}.bind(this));
},
valuesWithKeyMatch: function(match) {

@@ -223,3 +194,3 @@ match = match || /.*/;

if (filter(k)) {
values.push(this.data[k]);
values.push(this.data[k].value);
}

@@ -231,3 +202,3 @@ }.bind(this));

set: function () {
set: function (key, value, callback) {
return this.setItem(key, value, callback);

@@ -240,4 +211,3 @@ },

var options = this.options;
var result;
var logmsg = "set (" + key + ": " + options.stringify(value) + ")";
var logmsg = "set (" + key + ": " + this.stringify(value) + ")";

@@ -247,8 +217,6 @@ var deferred = Q.defer();

this.data[key] = value;
if (options.ttl) {
this.ttls[key] = new Date().getTime() + options.ttl;
}
var ttl = options.ttl ? new Date().getTime() + options.ttl : undefined;
this.data[key] = {value: value, ttl: ttl};
result = {key: key, value: value, queued: !!options.interval, manual: !options.interval && !options.continuous};
var result = {key: key, value: value, ttl: ttl, queued: !!options.interval, manual: !options.interval && !options.continuous};

@@ -287,8 +255,6 @@ var onSuccess = function () {

setItemSync: function (key, value) {
this.data[key] = value;
if (this.options.ttl) {
this.ttls[key] = new Date().getTime() + this.options.ttl;
}
var ttl = this.options.ttl ? new Date().getTime() + this.options.ttl: undefined;
this.data[key] = {key: key, value: value, ttl: ttl};
this.persistKeySync(key);
this.log("set (" + key + ": " + this.options.stringify(value) + ")");
this.log("set (" + key + ": " + this.stringify(value) + ")");
},

@@ -314,4 +280,4 @@

} else {
callback(null, this.data[key]);
deferred.resolve(this.data[key]);
callback(null, this.data[key] && this.data[key].value);
deferred.resolve(this.data[key] && this.data[key].value);
}

@@ -325,3 +291,3 @@ return deferred.promise;

} else {
return this.data[key];
return this.data[key] && this.data[key].value;
}

@@ -348,7 +314,7 @@ },

function() {
var value = this.data[key].value;
delete this.data[key];
delete this.ttls[key];
this.log('removed: ' + key);
callback(null, this.data);
deferred.resolve(this.data);
callback(null, value);
deferred.resolve(value);
}.bind(this),

@@ -360,3 +326,2 @@ function(err) {

);
return deferred.promise;

@@ -366,6 +331,7 @@ },

removeItemSync: function (key) {
var value = this.data[key].value;
this.removePersistedKeySync(key);
delete this.data[key];
delete this.ttls[key];
this.log('removed: ' + key);
return value;
},

@@ -377,3 +343,2 @@

var deferred = Q.defer();
var result;
var deferreds = [];

@@ -387,8 +352,7 @@

Q.all(deferreds).then(
function(result) {
function() {
this.data = {};
this.ttls = {};
this.changes = {};
deferred.resolve(result);
callback(null, result);
deferred.resolve();
callback();
}.bind(this),

@@ -409,3 +373,2 @@ function(err) {

this.data = {};
this.ttls = {};
this.changes = {};

@@ -458,49 +421,19 @@ },

var options = this.options;
var json = options.stringify(this.data[key]);
var file = path.join(options.dir, md5(key));
var file = path.join(options.dir, sanitize(key));
var ttlFile;
var deferred = Q.defer();
var result;
var output = {key: key, value: this.data[key].value, file: file, ttl: this.data[key].ttl};
var fail = function(err) {
self.changes[key] && self.changes[key].onError && self.changes[key].onError(err);
deferred.reject(err);
return callback(err);
};
fs.writeFile(file, this.stringify(output), options.encoding, function(err) {
if (err) {
self.changes[key] && self.changes[key].onError && self.changes[key].onError(err);
deferred.reject(err);
return callback(err);
}
var done = function() {
self.changes[key] && self.changes[key].onSuccess && self.changes[key].onSuccess();
delete self.changes[key];
deferred.resolve(output);
callback(null, output);
self.log("wrote: " + key);
result = {key: key, data: json, file: file};
deferred.resolve(result);
callback(null, result);
};
mkdirp(path.dirname(file), function(err) {
if (err) {
fail(err);
}
fs.writeFile(file, json, options.encoding, function(err) {
if (err) {
fail(err);
}
if (options.ttl) {
ttlFile = path.join(options.ttlDir, sanitize(key));
mkdirp(path.dirname(ttlFile), function(err) {
fs.writeFile(ttlFile, options.stringify(self.ttls[key]), options.encoding, function() {
if (err) {
fail(err);
} else {
done();
}
});
});
} else {
done();
}
}.bind(this));
});

@@ -513,19 +446,12 @@

var options = this.options;
var file = path.join(options.dir, sanitize(key));
var file = path.join(options.dir, md5(key));
var output = {key: key, value: this.data[key].value, file: file, ttl: this.data[key].ttl};
try {
mkdirp.sync(path.dirname(file));
fs.writeFileSync(file, options.stringify(this.data[key]));
fs.writeFileSync(file, this.stringify(output));
this.changes[key] && this.changes[key].onSuccess && this.changes[key].onSuccess();
} catch (e) {
this.changes[key] && this.changes[key].onError && this.changes[key].onError(e);
throw e;
} catch (err) {
this.changes[key] && this.changes[key].onError && this.changes[key].onError(err);
throw err;
}
var ttlFile;
if (options.ttl) {
ttlFile = path.join(options.ttlDir, sanitize(key));
mkdirp.sync(path.dirname(ttlFile));
fs.writeFileSync(ttlFile, options.stringify(this.ttls[key]));
}
delete this.changes[key];

@@ -543,42 +469,16 @@ this.log("wrote: " + key);

//check to see if key has been persisted
var file = path.join(options.dir, sanitize(key));
var file = path.join(options.dir, md5(key));
fs.exists(file, function (exists) {
if (exists) {
fs.unlink(file, function (err) {
result = {key: key, removed: !err, exists: true};
var fail = function(err) {
result = {key: key, removed: !err, existed: exists};
if (err) {
deferred.reject(err);
callback(err);
};
var done = function() {
deferred.resolve(result);
callback(null, result);
};
if (err) {
return fail(err);
return callback(err);
}
if (options.ttl) {
var ttlFile = path.join(options.ttlDir, sanitize(key));
fs.exists(ttlFile, function (exists) {
if (exists) {
fs.unlink(ttlFile, function (err) {
if (err) {
fail(err);
}
done();
});
} else {
done();
}
});
} else {
done();
}
deferred.resolve(result);
callback(null, result);
});
} else {
result = {key: key, removed: false, exists: false};
result = {key: key, removed: false, existed: exists};
deferred.resolve(result);

@@ -592,7 +492,21 @@ callback(null, result);

parseString: function(str){
removePersistedKeySync: function(key) {
var options = this.options;
var file = path.join(options.dir, md5(key));
if (fs.existsSync(file)) {
fs.unlinkSync(file);
return {key: key, removed: true, existed: true};
}
return {key: key, removed: false, existed: false};
},
stringify: function (obj) {
return this.options.stringify(obj);
},
parse: function(str){
try {
return this.options.parse(str);
} catch(e) {
this.log("parse error: ", this.options.stringify(e));
this.log("parse error: ", this.stringify(e));
return undefined;

@@ -602,19 +516,3 @@ }

parseTTLDir: function(callback) {
return this.parseDir(this.options.ttlDir, this.parseTTLFile.bind(this), callback);
},
parseTTLDirSync: function() {
return this.parseDirSync(this.options.ttlDir, this.ttls);
},
parseDataDir: function(callback) {
return this.parseDir(this.options.dir, this.parseDataFile.bind(this), callback);
},
parseDataDirSync: function() {
return this.parseDirSync(this.options.dir, this.data);
},
parseDir: function(dir, parseFn, callback) {
parseStorageDir: function(callback) {
callback = isFunction(callback) ? callback : noop;

@@ -625,3 +523,6 @@

var dir = this.options.dir;
var self = this;
var result = {dir: dir};
//check to see if dir is present

@@ -638,5 +539,5 @@ fs.exists(dir, function (exists) {

for (var i in arr) {
var curr = arr[i];
if (curr[0] !== '.') {
deferreds.push(parseFn(unsanitize(curr)));
var currentFile = arr[i];
if (currentFile[0] !== '.') {
deferreds.push(self.parseFile(currentFile));
}

@@ -654,4 +555,3 @@ }

});
}.bind(this));
});
} else {

@@ -665,9 +565,9 @@ //create the directory

} else {
this.log('created ' + dir);
self.log('created ' + dir);
deferred.resolve(result);
callback(null, result);
}
}.bind(this));
});
}
}.bind(this));
});

@@ -677,3 +577,4 @@ return deferred.promise;

parseDirSync: function(dir, hash) {
parseStorageDirSync: function() {
var dir = this.options.dir;
var exists = fs.existsSync(dir);

@@ -684,6 +585,5 @@

for (var i = 0; i < arr.length; i++) {
var curr = arr[i];
if (arr[i] && curr[0] !== '.') {
var json = fs.readFileSync(path.join(dir, curr), this.options.encoding);
hash[unsanitize(curr)] = this.parseString(json);
var currentFile = arr[i];
if (arr[i] && currentFile[0] !== '.') {
this.parseFileSync(currentFile);
}

@@ -696,27 +596,12 @@ }

parseDataFile: function(key, callback) {
return this.parseFile(key, this.options.dir, this.data, callback);
},
parseDataFileSync: function(key) {
return this.parseFileSync(key, this.options.dir, this.data);
},
parseTTLFile : function(key, callback) {
return this.parseFile(key, this.options.ttlDir, this.ttls, callback);
},
parseTTLFileSync: function(key) {
return this.parseFileSync(key, this.options.ttlDir, this.ttls);
},
parseFile: function (key, dir, hash, callback) {
parseFile: function (filename, callback) {
callback = isFunction(callback) ? callback : noop;
var deferred = Q.defer();
var result;
var file = path.join(dir, sanitize(key));
var self = this;
var options = this.options;
var dir = this.options.dir;
var file = path.join(dir, filename);
fs.readFile(file, options.encoding, function (err, json) {
fs.readFile(file, options.encoding, function (err, text) {
if (err) {

@@ -726,23 +611,19 @@ deferred.reject(err);

}
var input = self.parse(text);
self.data[input.key] = input;
self.log("loaded: " + dir + "/" + input.key);
deferred.resolve(input);
callback(null, input);
});
var value = this.parseString(json);
hash[key] = value;
this.log("loaded: " + dir + "/" + key);
result = {key: key, value: value, file: file};
deferred.resolve(result);
callback(null, result);
}.bind(this));
return deferred.promise;
},
parseFileSync: function(key, dir, hash) {
var file = path.join(dir, sanitize(key));
hash[key] = fs.readFileSync(file, this.options.encoding);
this.log("loaded: " + dir + "/" + key);
return hash[key];
parseFileSync: function(filename) {
var dir = this.options.dir;
var file = path.join(dir, filename);
var input = this.parse(fs.readFileSync(file, this.options.encoding));
this.data[input.key] = input;
this.log("loaded: " + dir + "/" + input.key);
return this.data[input.key];
},

@@ -752,20 +633,5 @@

if (!this.options.ttl) return false;
return this.ttls[key] < (new Date()).getTime();
return this.data[key] && this.data[key].ttl && this.data[key].ttl < (new Date()).getTime();
},
removePersistedKeySync: function(key) {
var options = this.options;
var file = path.join(options.dir, sanitize(key));
if (fs.existsSync(file)) {
fs.unlinkSync(file);
}
if (options.ttl) {
var ttlFile = path.join(options.ttlDir, sanitize(key));
if (fs.existsSync(ttlFile)) {
fs.unlinkSync(ttlFile);
}
}
},
resolveDir: function(dir) {

@@ -787,6 +653,5 @@ dir = path.normalize(dir);

sanitize: sanitize,
unsanitize: unsanitize
md5: md5
};
module.exports = LocalStorage;

@@ -22,8 +22,2 @@ /*

/*
* All functions below are just helpers to use the default storage instance
* and to maintain backward compatibility
*/
/*
* This function, (or init) must be called before the library can be used.

@@ -33,4 +27,6 @@ * An options hash can be optionally passed.

nodePersist.init = function (userOptions, callback) {
localStorage = nodePersist.create(userOptions);
return localStorage.init(callback);
localStorage = nodePersist.defaultInstance = nodePersist.create(userOptions);
var ret = localStorage.init(callback);
mixin(nodePersist, localStorage, {skip: ['init', 'initSync', 'create']});
return ret;
};

@@ -42,136 +38,20 @@ /*

nodePersist.initSync = function (userOptions) {
localStorage = nodePersist.create(userOptions);
return localStorage.initSync();
localStorage = nodePersist.defaultInstance = nodePersist.create(userOptions);
var ret = localStorage.initSync();
mixin(nodePersist, localStorage, {skip: ['init', 'initSync', 'create']});
return ret;
};
/*
* This function returns a key with index n in the database, or null if
* it is not present.
* This function runs in 0(k), where k is the number of keys in the
* database. You probably shouldn't use it.
*/
nodePersist.key = function (n) {
return localStorage.key(n);
};
// expose all the API methods on the main module using a default instance
function mixin (target, source, options) {
options = options || {};
options.skip = options.skip || [];
var key;
for (key in source) {
if (typeof source[key] === 'function' && key.indexOf('_') !== 0 && options.skip.indexOf(key) == -1) {
target[key] = source[key].bind(source);
}
}
}
/*
* This function returns an array of all the keys in the database
*
*/
nodePersist.keys = function () {
return localStorage.keys();
};
/*
* This function returns the number of keys stored in the database.
*/
nodePersist.length = function () {
return localStorage.length();
};
/*
* This function iterates over each key/value pair and executes a callback
*/
nodePersist.forEach = function(callback) {
return localStorage.forEach(callback);
};
/*
* This function returns all the values in the database.
*/
nodePersist.values = function(callback) {
return localStorage.values(callback);
};
nodePersist.valuesWithKeyMatch = function(match, callback) {
return localStorage.valuesWithKeyMatch(match, callback);
};
/*
* This function sets a key to a given value in the database.
*/
nodePersist.setItem = function (key, value, callback) {
return localStorage.setItem(key, value, callback);
};
/*
* This function sets a key to a given value in the database.
*/
nodePersist.setItemSync = function (key, value) {
return localStorage.setItemSync(key, value);
};
/*
* This function returns the value associated with a key in the database,
* or undefined if it is not present.
*/
nodePersist.getItem = function (key, callback) {
return localStorage.getItem(key, callback);
};
nodePersist.getItemSync = function (key) {
return localStorage.getItemSync(key);
};
/*
* This function removes key in the database if it is present, and
* immediately deletes it from the file system asynchronously.
*/
nodePersist.removeItem = function (key, callback) {
return localStorage.removeItem(key, callback);
};
/*
* This function removes key in the database if it is present, and
* immediately deletes it from the file system synchronously.
*/
nodePersist.removeItemSync = function (key) {
return localStorage.removeItemSync(key);
};
/*
* This function removes all keys in the database, and immediately
* deletes all keys from the file system asynchronously.
*/
nodePersist.clear = function (callback) {
return localStorage.clear(callback);
};
/*
* This function removes all keys in the database, and immediately
* deletes all keys from the file system synchronously.
*/
nodePersist.clearSync = function () {
return localStorage.clearSync();
};
/*
* This function triggers the database to persist asynchronously.
*/
nodePersist.persist = function (callback) {
return localStorage.persist(callback);
};
/*
* This function triggers the database to persist synchronously.
*/
nodePersist.persistSync = function () {
return localStorage.persistSync();
};
/*
* This function triggers a key within the database to persist asynchronously.
*/
nodePersist.persistKey = function (key, callback) {
return localStorage.persistKey(key, callback);
};
/*
* This function triggers a key within the database to persist synchronously.
*/
nodePersist.persistKeySync = function (key) {
return localStorage.persistKeySync(key);
};
}(module.exports));

@@ -28,8 +28,9 @@

describe("instances", function() {
var dir1 = randDir();
var storage1 = nodePersist.create({
dir: randDir()
dir: dir1
});
var dir2 = randDir();
var storage2 = nodePersist.create({
dir: randDir()
dir: dir2
});

@@ -40,8 +41,41 @@

it("should create 2 new different instances of LocalStorage", function() {
it("should create 2 new different instances of LocalStorage", function(done) {
assert.ok(storage1 instanceof LocalStorage);
assert.ok(storage2 instanceof LocalStorage);
assert.ok(storage1 != storage2);
done();
});
storage1.setItemSync("s1", 1111);
storage2.setItemSync("s2", 2222);
var storage11 = nodePersist.create({
dir: dir1
});
var storage22 = nodePersist.create({
dir: dir2
});
it("should use the 2 previous dirs and initSync correctly", function(done) {
storage11.initSync();
assert.equal(storage11.getItemSync("s1"), 1111, "write/read didn't work");
storage22.init().then(function() {
storage2.getItem("s2").then(function(value) {
assert.equal(value, 2222, "write/read didn't work");
done();
})
});
});
it("should create the default instance of LocalStorage sync and use it", function(done) {
nodePersist.initSync({
dir: randDir()
});
assert.ok(nodePersist.defaultInstance instanceof LocalStorage);
nodePersist.setItemSync("item8877", "hello");
assert.equal(nodePersist.getItemSync("item8877"), 'hello', "write/read didn't work");
done();
});
it("should create a default instance", function(done) {

@@ -287,3 +321,3 @@ var dir = randDir();

assert.approximately(endTime, startTime, 2500, "within 2.5s or so");
assert.equal(true, fs.existsSync(storage.options.dir + "/" + storage.sanitize("item999")));
assert.equal(true, fs.existsSync(storage.options.dir + "/" + storage.md5("item999")));
done();

@@ -293,3 +327,3 @@ });

// check if the item1 file exists immediately, it shouldnt
assert.notEqual(true, fs.existsSync(storage.options.dir + "/" + storage.sanitize("item999")));
assert.notEqual(true, fs.existsSync(storage.options.dir + "/" + storage.md5("item999")));
});

@@ -296,0 +330,0 @@ });

Sorry, the diff of this file is not supported yet

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