Socket
Socket
Sign inDemoInstall

nedb

Package Overview
Dependencies
Maintainers
1
Versions
95
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nedb - npm Package Compare versions

Comparing version 1.3.0 to 1.4.0

browser-version/test/chai.js

81

browser-version/browser-specific/lib/storage.js
/**
* Way data is stored for this database
* For a Node.js/Node Webkit database it's the file system
* For a browser-side database it's localStorage when supported
* For a browser-side database it's localforage, which uses the best backend available (IndexedDB then WebSQL then localStorage)
*

@@ -9,13 +9,19 @@ * This version is the browser version

var localforage = require('localforage')
// Configure localforage to display NeDB name for now. Would be a good idea to let user use his own app name
localforage.config({
name: 'NeDB'
, storeName: 'nedbdata'
});
function exists (filename, callback) {
// In this specific case this always answers that the file doesn't exist
if (typeof localStorage === 'undefined') { console.log("WARNING - This browser doesn't support localStorage, no data will be saved in NeDB!"); return callback(); }
if (localStorage.getItem(filename) !== null) {
return callback(true);
} else {
return callback(false);
}
localforage.getItem(filename, function (err, value) {
if (value !== null) { // Even if value is undefined, localforage returns null
return callback(true);
} else {
return callback(false);
}
});
}

@@ -25,12 +31,11 @@

function rename (filename, newFilename, callback) {
if (typeof localStorage === 'undefined') { console.log("WARNING - This browser doesn't support localStorage, no data will be saved in NeDB!"); return callback(); }
if (localStorage.getItem(filename) === null) {
localStorage.removeItem(newFilename);
} else {
localStorage.setItem(newFilename, localStorage.getItem(filename));
localStorage.removeItem(filename);
}
return callback();
localforage.getItem(filename, function (err, value) {
if (value === null) {
localforage.removeItem(newFilename, function () { return callback(); });
} else {
localforage.setItem(newFilename, value, function () {
localforage.removeItem(filename, function () { return callback(); });
});
}
});
}

@@ -40,9 +45,5 @@

function writeFile (filename, contents, options, callback) {
if (typeof localStorage === 'undefined') { console.log("WARNING - This browser doesn't support localStorage, no data will be saved in NeDB!"); return callback(); }
// Options do not matter in browser setup
if (typeof options === 'function') { callback = options; }
localStorage.setItem(filename, contents);
return callback();
localforage.setItem(filename, contents, function () { return callback(); });
}

@@ -52,12 +53,10 @@

function appendFile (filename, toAppend, options, callback) {
if (typeof localStorage === 'undefined') { console.log("WARNING - This browser doesn't support localStorage, no data will be saved in NeDB!"); return callback(); }
// Options do not matter in browser setup
if (typeof options === 'function') { callback = options; }
var contents = localStorage.getItem(filename) || '';
contents += toAppend;
localStorage.setItem(filename, contents);
return callback();
localforage.getItem(filename, function (err, contents) {
contents = contents || '';
contents += toAppend;
localforage.setItem(filename, contents, function () { return callback(); });
});
}

@@ -67,9 +66,5 @@

function readFile (filename, options, callback) {
if (typeof localStorage === 'undefined') { console.log("WARNING - This browser doesn't support localStorage, no data will be saved in NeDB!"); return callback(); }
// Options do not matter in browser setup
if (typeof options === 'function') { callback = options; }
var contents = localStorage.getItem(filename) || '';
return callback(null, contents);
localforage.getItem(filename, function (err, contents) { return callback(null, contents || ''); });
}

@@ -79,10 +74,7 @@

function unlink (filename, callback) {
if (typeof localStorage === 'undefined') { console.log("WARNING - This browser doesn't support localStorage, no data will be saved in NeDB!"); return callback(); }
localStorage.removeItem(filename);
return callback();
localforage.removeItem(filename, function () { return callback(); });
}
// Nothing done, no directories will be used on the browser
// Nothing to do, no directories will be used on the browser
function mkdirp (dir, callback) {

@@ -93,3 +85,8 @@ return callback();

// Nothing to do, no data corruption possible in the brower
function ensureDatafileIntegrity (filename, callback) {
return callback(null);
}
// Interface

@@ -99,2 +96,3 @@ module.exports.exists = exists;

module.exports.writeFile = writeFile;
module.exports.crashSafeWriteFile = writeFile; // No need for a crash safe function in the browser
module.exports.appendFile = appendFile;

@@ -104,2 +102,3 @@ module.exports.readFile = readFile;

module.exports.mkdirp = mkdirp;
module.exports.ensureDatafileIntegrity = ensureDatafileIntegrity;

@@ -298,15 +298,6 @@ /**

describe('Persistent in-browser database', function () {
describe("Don't forget to launch persistence tests!", function () {
it('Create a localStorage-enabled database and insert to it, results are recorded in localStorage', function (done) {
localStorage.removeItem('test'); // Clean start state
var d = new Nedb({ filename: 'test', autoload: true });
d.insert({ test: true }, function (err) {
assert.isNull(err);
var contents = localStorage.getItem('test');
contents = JSON.parse(contents);
assert.equal(contents.test, true);
done();
});
it("See file testPersistence.html", function (done) {
done();
});

@@ -313,0 +304,0 @@

@@ -8,12 +8,14 @@ console.log("Beginning tests");

localStorage.removeItem('test');
var db = new Nedb({ filename: 'test', autoload: true });
db.insert({ hello: 'world' }, function (err) {
if (err) {
testsFailed();
return;
}
var filename = 'test';
window.location = './testPersistence2.html';
var db = new Nedb({ filename: filename, autoload: true });
db.remove({}, { multi: true }, function () {
db.insert({ hello: 'world' }, function (err) {
if (err) {
testsFailed();
return;
}
window.location = './testPersistence2.html';
});
});

@@ -0,1 +1,10 @@

// Capture F5 to reload the base page testPersistence.html not this one
$(document).on('keydown', function (e) {
if (e.keyCode === 116) {
e.preventDefault();
window.location = 'testPersistence.html';
}
});
console.log("Checking tests results");

@@ -8,5 +17,8 @@ console.log("Please note these tests work on Chrome latest, might not work on other browsers due to discrepancies in how local storage works for the file:// protocol");

var db = new Nedb({ filename: 'test', autoload: true });
var filename = 'test';
var db = new Nedb({ filename: filename, autoload: true });
db.find({}, function (err, docs) {
if (docs.length !== 1) {
console.log(docs);
console.log("Unexpected length of document database");

@@ -13,0 +25,0 @@ return testsFailed();

@@ -25,3 +25,3 @@ /**

var i, j, randomString;
this.db = options.db;

@@ -31,10 +31,5 @@ this.inMemoryOnly = this.db.inMemoryOnly;

this.corruptAlertThreshold = options.corruptAlertThreshold !== undefined ? options.corruptAlertThreshold : 0.1;
if (!this.inMemoryOnly && this.filename) {
if (this.filename.charAt(this.filename.length - 1) === '~') {
throw "The datafile name can't end with a ~, which is reserved for automatic backup files";
} else {
this.tempFilename = this.filename + '~';
this.oldFilename = this.filename + '~~';
}
if (!this.inMemoryOnly && this.filename && this.filename.charAt(this.filename.length - 1) === '~') {
throw "The datafile name can't end with a ~, which is reserved for crash safe backup files";
}

@@ -59,3 +54,3 @@

}
// For NW apps, store data in the same directory where NW stores application data

@@ -71,4 +66,2 @@ if (this.filename && options.nodeWebkitAppName) {

this.filename = Persistence.getNWAppFilename(options.nodeWebkitAppName, this.filename);
this.tempFilename = Persistence.getNWAppFilename(options.nodeWebkitAppName, this.tempFilename);
this.oldFilename = Persistence.getNWAppFilename(options.nodeWebkitAppName, this.oldFilename);
}

@@ -90,9 +83,2 @@ };

Persistence.ensureFileDoesntExist = function (file, callback) {
storage.exists(file, function (exists) {
if (!exists) { return callback(null); }
storage.unlink(file, function (err) { return callback(err); });
});
};

@@ -145,3 +131,3 @@

if (this.inMemoryOnly) { return callback(null); }
if (this.inMemoryOnly) { return callback(null); }

@@ -157,22 +143,3 @@ this.db.getAllData().forEach(function (doc) {

async.waterfall([
async.apply(Persistence.ensureFileDoesntExist, self.tempFilename)
, async.apply(Persistence.ensureFileDoesntExist, self.oldFilename)
, function (cb) {
storage.exists(self.filename, function (exists) {
if (exists) {
storage.rename(self.filename, self.oldFilename, function (err) { return cb(err); });
} else {
return cb();
}
});
}
, function (cb) {
storage.writeFile(self.tempFilename, toPersist, function (err) { return cb(err); });
}
, function (cb) {
storage.rename(self.tempFilename, self.filename, function (err) { return cb(err); });
}
, async.apply(Persistence.ensureFileDoesntExist, self.oldFilename)
], function (err) { if (err) { return callback(err); } else { return callback(null); } })
storage.crashSafeWriteFile(this.filename, toPersist, callback);
};

@@ -254,6 +221,6 @@

;
for (i = 0; i < data.length; i += 1) {
var doc;
try {

@@ -276,3 +243,3 @@ doc = model.deserialize(this.beforeDeserialization(data[i]));

}
// A bit lenient on corruption

@@ -292,26 +259,2 @@ if (data.length > 0 && corruptItems / data.length > this.corruptAlertThreshold) {

/**
* Ensure that this.filename contains the most up-to-date version of the data
* Even if a loadDatabase crashed before
*/
Persistence.prototype.ensureDatafileIntegrity = function (callback) {
var self = this ;
storage.exists(self.filename, function (filenameExists) {
// Write was successful
if (filenameExists) { return callback(null); }
storage.exists(self.oldFilename, function (oldFilenameExists) {
// New database
if (!oldFilenameExists) {
return storage.writeFile(self.filename, '', 'utf8', function (err) { callback(err); });
}
// Write failed, use old version
storage.rename(self.oldFilename, self.filename, function (err) { return callback(err); });
});
});
};
/**
* Load the database

@@ -339,6 +282,6 @@ * 1) Create all indexes

Persistence.ensureDirectoryExists(path.dirname(self.filename), function (err) {
self.ensureDatafileIntegrity(function (exists) {
storage.ensureDatafileIntegrity(self.filename, function (err) {
storage.readFile(self.filename, 'utf8', function (err, rawData) {
if (err) { return cb(err); }
try {

@@ -349,3 +292,3 @@ var treatedData = self.treatRawData(rawData);

}
// Recreate all indexes in the datafile

@@ -352,0 +295,0 @@ Object.keys(treatedData.indexes).forEach(function (key) {

/**
* Way data is stored for this database
* For a Node.js/Node Webkit database it's the file system
* For a browser-side database it's localStorage when supported
* For a browser-side database it's localforage which chooses the best option depending on user browser (IndexedDB then WebSQL then localStorage)
*
* This version is the Node.js/Node Webkit version
* It's essentially fs, mkdirp and crash safe write and read functions
*/

@@ -11,6 +12,91 @@

, mkdirp = require('mkdirp')
, async = require('async')
, storage = {}
;
storage.exists = fs.exists;
storage.rename = fs.rename;
storage.writeFile = fs.writeFile;
storage.unlink = fs.unlink;
storage.appendFile = fs.appendFile;
storage.readFile = fs.readFile;
storage.mkdirp = mkdirp;
module.exports = fs;
module.exports.mkdirp = mkdirp;
/**
* Explicit name ...
*/
storage.ensureFileDoesntExist = function (file, callback) {
storage.exists(file, function (exists) {
if (!exists) { return callback(null); }
storage.unlink(file, function (err) { return callback(err); });
});
};
/**
* Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost)
* @param {String} filename
* @param {String} data
* @param {Function} cb Optional callback, signature: err
*/
storage.crashSafeWriteFile = function (filename, data, cb) {
var callback = cb || function () {}
, tempFilename = filename + '~'
, oldFilename = filename + '~~'
;
async.waterfall([
async.apply(storage.ensureFileDoesntExist, tempFilename)
, async.apply(storage.ensureFileDoesntExist, oldFilename)
, function (cb) {
storage.exists(filename, function (exists) {
if (exists) {
storage.rename(filename, oldFilename, function (err) { return cb(err); });
} else {
return cb();
}
});
}
, function (cb) {
storage.writeFile(tempFilename, data, function (err) { return cb(err); });
}
, function (cb) {
storage.rename(tempFilename, filename, function (err) { return cb(err); });
}
, async.apply(storage.ensureFileDoesntExist, oldFilename)
], function (err) { if (err) { return callback(err); } else { return callback(null); } })
};
/**
* Ensure the datafile contains all the data, even if there was a crash during a full file write
* @param {String} filename
* @param {Function} callback signature: err
*/
storage.ensureDatafileIntegrity = function (filename, callback) {
var tempFilename = filename + '~'
, oldFilename = filename + '~~'
;
storage.exists(filename, function (filenameExists) {
// Write was successful
if (filenameExists) { return callback(null); }
storage.exists(oldFilename, function (oldFilenameExists) {
// New database
if (!oldFilenameExists) {
return storage.writeFile(filename, '', 'utf8', function (err) { callback(err); });
}
// Write failed, use old version
storage.rename(oldFilename, filename, function (err) { return callback(err); });
});
});
};
// Interface
module.exports = storage;
{
"name": "nedb",
"version": "1.3.0",
"version": "1.4.0",
"author": {

@@ -25,2 +25,3 @@ "name": "Louis Chatriot",

"binary-search-tree": "0.2.4",
"localforage": "^1.3.0",
"mkdirp": "~0.5.1",

@@ -27,0 +28,0 @@ "underscore": "~1.4.4"

@@ -579,3 +579,3 @@ <img src="http://i.imgur.com/9O1xHFb.png" style="width: 25%; height: 25%; float: left;">

## Browser version
As of v0.8.0, you can use NeDB in the browser! You can find it and its minified version in the repository, in the `browser-version/out` directory. You only need to require `nedb.js` or `nedb.min.js` in your HTML file and the global object `Nedb` can be used right away, with the same API as the server version:
The browser version and its minified counterpart are in the `browser-version/out` directory. You only need to require `nedb.js` or `nedb.min.js` in your HTML file and the global object `Nedb` can be used right away, with the same API as the server version:

@@ -587,7 +587,6 @@ ```

db.insert({ planet: 'Earth' });
db.insert({ planet: 'Mars' });
db.find({}, function (err, docs) {
// docs contains the two planets Earth and Mars
db.insert({ planet: 'Earth' }, function (err) {
db.find({}, function (err, docs) {
// docs contains the two planets Earth and Mars
});
});

@@ -597,11 +596,9 @@ </script>

It has been tested and is compatible with Chrome, Safari, Firefox, IE 10, IE 9. Please open an issue if you need compatibility with IE 8/IE 7, I think it will need some work and am not sure it is needed, since most complex webapplications - the ones that would need NeDB - only work on modern browsers anyway. To launch the tests, simply open the file `browser-version/test/index.html` in a browser and you'll see the results of the tests for this browser.
If you specify a `filename`, the database will be persistent, and automatically select the best storage method available (IndexedDB, WebSQL or localStorage) depending on the browser.
NeDB is compatible with all major browsers: Chrome, Safari, Firefox, IE9+. Tests are in the `browser-version/test` directory (files `index.html` and `testPersistence.html`).
If you fork and modify nedb, you can build the browser version from the sources, the build script is `browser-version/build.js`.
As of v0.11, NeDB is also persistent on the browser. To use this, simply create the collection with the `filename` option which will be the name of the `localStorage` variable storing data. Persistence should work on all browsers where NeDB works. Also, keep in mind that `localStorage` has size constraints, so it's probably a good idea to set recurring compaction every 2-5 minutes to save on space if your client app needs a lot of updates and deletes. See <a href="#compacting-the-database">database compaction</a> for more details on the append-only format used by NeDB.
**Browser persistence is still young! It has been tested on most major browsers but please report any bugs you find**
## Performance

@@ -608,0 +605,0 @@ ### Speed

@@ -12,4 +12,5 @@ var should = require('chai').should()

, Persistence = require('../lib/persistence')
, storage = require('../lib/storage')
, child_process = require('child_process')
;
;

@@ -36,8 +37,8 @@

, function (cb) {
d.loadDatabase(function (err) {
assert.isNull(err);
d.getAllData().length.should.equal(0);
return cb();
});
}
d.loadDatabase(function (err) {
assert.isNull(err);
d.getAllData().length.should.equal(0);
return cb();
});
}
], done);

@@ -49,6 +50,6 @@ });

, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } })
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } })
, treatedData = d.persistence.treatRawData(rawData).data
;
;

@@ -65,6 +66,6 @@ treatedData.sort(function (a, b) { return a._id - b._id; });

, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
'garbage\n' +
model.serialize({ _id: "3", nested: { today: now } })
'garbage\n' +
model.serialize({ _id: "3", nested: { today: now } })
, treatedData = d.persistence.treatRawData(rawData).data
;
;

@@ -80,6 +81,6 @@ treatedData.sort(function (a, b) { return a._id - b._id; });

, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ nested: { today: now } })
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ nested: { today: now } })
, treatedData = d.persistence.treatRawData(rawData).data
;
;

@@ -95,6 +96,6 @@ treatedData.sort(function (a, b) { return a._id - b._id; });

, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", nested: { today: now } })
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", nested: { today: now } })
, treatedData = d.persistence.treatRawData(rawData).data
;
;

@@ -110,7 +111,7 @@ treatedData.sort(function (a, b) { return a._id - b._id; });

, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = d.persistence.treatRawData(rawData).data
;
;

@@ -126,6 +127,6 @@ treatedData.sort(function (a, b) { return a._id - b._id; });

, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
model.serialize({ _id: "2", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = d.persistence.treatRawData(rawData).data
;
;

@@ -137,15 +138,15 @@ treatedData.sort(function (a, b) { return a._id - b._id; });

});
it('If a doc contains $$indexCreated, no error is thrown during treatRawData and we can get the index options', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ $$indexCreated: { fieldName: "test", unique: true } }) + '\n' +
model.serialize({ _id: "3", today: now })
model.serialize({ $$indexCreated: { fieldName: "test", unique: true } }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = d.persistence.treatRawData(rawData).data
, indexes = d.persistence.treatRawData(rawData).indexes
;
;
Object.keys(indexes).length.should.equal(1);
assert.deepEqual(indexes.test, { fieldName: "test", unique: true });
treatedData.sort(function (a, b) { return a._id - b._id; });

@@ -193,3 +194,3 @@ treatedData.length.should.equal(2);

, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
;
assert.isNull(err);

@@ -204,3 +205,3 @@ data.length.should.equal(2);

, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
;
assert.isNull(err);

@@ -226,3 +227,3 @@ data.length.should.equal(2);

, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
;
assert.isNull(err);

@@ -255,3 +256,3 @@ data.length.should.equal(2);

, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
;
assert.isNull(err);

@@ -266,5 +267,5 @@ data.length.should.equal(2);

var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
, doc3 = _.find(data, function (doc) { return doc.a === 3; })
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
, doc3 = _.find(data, function (doc) { return doc.a === 3; })
;

@@ -289,3 +290,3 @@ assert.isNull(err);

, d
;
;
fs.writeFileSync(corruptTestFilename, fakeData, "utf8");

@@ -303,3 +304,3 @@

assert.isNull(err);
fs.writeFileSync(corruptTestFilename, fakeData, "utf8");

@@ -314,28 +315,28 @@ d = new Datastore({ filename: corruptTestFilename, corruptAlertThreshold: 0 });

});
});
});
});
describe('Serialization hooks', function () {
var as = function (s) { return "before_" + s + "_after"; }
, bd = function (s) { return s.substring(7, s.length - 6); }
, bd = function (s) { return s.substring(7, s.length - 6); }
it("Declaring only one hook will throw an exception to prevent data loss", function (done) {
var hookTestFilename = 'workspace/hookTest.db'
Persistence.ensureFileDoesntExist(hookTestFilename, function () {
storage.ensureFileDoesntExist(hookTestFilename, function () {
fs.writeFileSync(hookTestFilename, "Some content", "utf8");
(function () {
new Datastore({ filename: hookTestFilename, autoload: true
, afterSerialization: as
});
});
}).should.throw();
// Data file left untouched
fs.readFileSync(hookTestFilename, "utf8").should.equal("Some content");
(function () {
new Datastore({ filename: hookTestFilename, autoload: true
, beforeDeserialization: bd
});
});
}).should.throw();

@@ -349,8 +350,8 @@

});
it("Declaring two hooks that are not reverse of one another will cause an exception to prevent data loss", function (done) {
var hookTestFilename = 'workspace/hookTest.db'
Persistence.ensureFileDoesntExist(hookTestFilename, function () {
storage.ensureFileDoesntExist(hookTestFilename, function () {
fs.writeFileSync(hookTestFilename, "Some content", "utf8");
(function () {

@@ -360,3 +361,3 @@ new Datastore({ filename: hookTestFilename, autoload: true

, beforeDeserialization: function (s) { return s; }
});
});
}).should.throw();

@@ -370,12 +371,12 @@

});
it("A serialization hook can be used to transform data before writing new state to disk", function (done) {
var hookTestFilename = 'workspace/hookTest.db'
Persistence.ensureFileDoesntExist(hookTestFilename, function () {
storage.ensureFileDoesntExist(hookTestFilename, function () {
var d = new Datastore({ filename: hookTestFilename, autoload: true
, afterSerialization: as
, beforeDeserialization: bd
})
;
, afterSerialization: as
, beforeDeserialization: bd
})
;
d.insert({ hello: "world" }, function () {

@@ -385,6 +386,6 @@ var _data = fs.readFileSync(hookTestFilename, 'utf8')

, doc0 = bd(data[0])
;
;
data.length.should.equal(2);
data[0].substring(0, 7).should.equal('before_');

@@ -402,6 +403,6 @@ data[0].substring(data[0].length - 6).should.equal('_after');

, doc1 = bd(data[1])
;
;
data.length.should.equal(3);
data[0].substring(0, 7).should.equal('before_');

@@ -426,6 +427,6 @@ data[0].substring(data[0].length - 6).should.equal('_after');

, idx = bd(data[2])
;
;
data.length.should.equal(4);
data[0].substring(0, 7).should.equal('before_');

@@ -438,3 +439,3 @@ data[0].substring(data[0].length - 6).should.equal('_after');

Object.keys(doc0).length.should.equal(2);
doc0.hello.should.equal('world');
doc0.hello.should.equal('world');

@@ -444,8 +445,8 @@ doc1 = model.deserialize(doc1);

doc1.p.should.equal('Mars');
idx = model.deserialize(idx);
assert.deepEqual(idx, { '$$indexCreated': { fieldName: 'idefix' } });
done();
});
});
});

@@ -455,14 +456,14 @@ });

});
it("Use serialization hook when persisting cached database or compacting", function (done) {
var hookTestFilename = 'workspace/hookTest.db'
Persistence.ensureFileDoesntExist(hookTestFilename, function () {
storage.ensureFileDoesntExist(hookTestFilename, function () {
var d = new Datastore({ filename: hookTestFilename, autoload: true
, afterSerialization: as
, beforeDeserialization: bd
})
;
, afterSerialization: as
, beforeDeserialization: bd
})
;
d.insert({ hello: "world" }, function () {
d.update({ hello: "world" }, { $set: { hello: "earth" } }, {}, function () {
d.update({ hello: "world" }, { $set: { hello: "earth" } }, {}, function () {
d.ensureIndex({ fieldName: 'idefix' }, function () {

@@ -475,10 +476,10 @@ var _data = fs.readFileSync(hookTestFilename, 'utf8')

, _id
;
;
data.length.should.equal(4);
doc0 = model.deserialize(doc0);
Object.keys(doc0).length.should.equal(2);
doc0.hello.should.equal('world');
doc0.hello.should.equal('world');
doc1 = model.deserialize(doc1);

@@ -490,3 +491,3 @@ Object.keys(doc1).length.should.equal(2);

_id = doc0._id;
idx = model.deserialize(idx);

@@ -500,6 +501,6 @@ assert.deepEqual(idx, { '$$indexCreated': { fieldName: 'idefix' } });

, idx = bd(data[1])
;
;
data.length.should.equal(3);
doc0 = model.deserialize(doc0);

@@ -510,6 +511,6 @@ Object.keys(doc0).length.should.equal(2);

doc0._id.should.equal(_id);
idx = model.deserialize(idx);
assert.deepEqual(idx, { '$$indexCreated': { fieldName: 'idefix', unique: false, sparse: false } });
done();

@@ -522,11 +523,11 @@ });

});
it("Deserialization hook is correctly used when loading data", function (done) {
var hookTestFilename = 'workspace/hookTest.db'
Persistence.ensureFileDoesntExist(hookTestFilename, function () {
storage.ensureFileDoesntExist(hookTestFilename, function () {
var d = new Datastore({ filename: hookTestFilename, autoload: true
, afterSerialization: as
, beforeDeserialization: bd
})
;
, afterSerialization: as
, beforeDeserialization: bd
})
;

@@ -541,3 +542,3 @@ d.insert({ hello: "world" }, function (err, doc) {

, data = _data.split('\n')
;
;

@@ -548,6 +549,6 @@ data.length.should.equal(6);

var d = new Datastore({ filename: hookTestFilename
, afterSerialization: as
, beforeDeserialization: bd
})
;
, afterSerialization: as
, beforeDeserialization: bd
})
;
d.loadDatabase(function () {

@@ -558,3 +559,3 @@ d.find({}, function (err, docs) {

docs[0]._id.should.equal(_id);
Object.keys(d.indexes).length.should.equal(2);

@@ -568,3 +569,3 @@ Object.keys(d.indexes).indexOf("idefix").should.not.equal(-1);

});
});
});
});

@@ -574,5 +575,5 @@ });

});
}); // ==== End of 'Serialization hooks' ==== //
describe('Prevent dataloss when persisting data', function () {

@@ -583,40 +584,40 @@

})
it('Creating a persistent datastore with a bad filename will cause an error', function () {
(function () { new Datastore({ filename: 'workspace/bad.db~' }); }).should.throw();
})
})
it('If no file exists, ensureDatafileIntegrity creates an empty datafile', function (done) {
var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } });
if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); }
if (fs.existsSync('workspace/it.db~~')) { fs.unlinkSync('workspace/it.db~~'); }
fs.existsSync('workspace/it.db').should.equal(false);
fs.existsSync('workspace/it.db~~').should.equal(false);
p.ensureDatafileIntegrity(function (err) {
fs.existsSync('workspace/it.db~~').should.equal(false);
storage.ensureDatafileIntegrity(p.filename, function (err) {
assert.isNull(err);
fs.existsSync('workspace/it.db').should.equal(true);
fs.existsSync('workspace/it.db~~').should.equal(false);
fs.readFileSync('workspace/it.db', 'utf8').should.equal('');
done();
});
});
it('If only datafile exists, ensureDatafileIntegrity will use it', function (done) {
var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } });
if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); }
if (fs.existsSync('workspace/it.db~~')) { fs.unlinkSync('workspace/it.db~~'); }
fs.writeFileSync('workspace/it.db', 'something', 'utf8');
fs.existsSync('workspace/it.db').should.equal(true);
fs.existsSync('workspace/it.db~~').should.equal(false);
p.ensureDatafileIntegrity(function (err) {
fs.existsSync('workspace/it.db~~').should.equal(false);
storage.ensureDatafileIntegrity(p.filename, function (err) {
assert.isNull(err);

@@ -626,52 +627,52 @@

fs.existsSync('workspace/it.db~~').should.equal(false);
fs.readFileSync('workspace/it.db', 'utf8').should.equal('something');
done();
});
});
it('If old datafile exists and datafile doesnt, ensureDatafileIntegrity will use it', function (done) {
var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } });
if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); }
if (fs.existsSync('workspace/it.db~~')) { fs.unlinkSync('workspace/it.db~~'); }
fs.writeFileSync('workspace/it.db~~', 'something', 'utf8');
fs.existsSync('workspace/it.db').should.equal(false);
fs.existsSync('workspace/it.db~~').should.equal(true);
p.ensureDatafileIntegrity(function (err) {
fs.existsSync('workspace/it.db~~').should.equal(true);
storage.ensureDatafileIntegrity(p.filename, function (err) {
assert.isNull(err);
fs.existsSync('workspace/it.db').should.equal(true);
fs.existsSync('workspace/it.db~~').should.equal(false);
fs.readFileSync('workspace/it.db', 'utf8').should.equal('something');
done();
});
});
it('If both old and current datafiles exist, ensureDatafileIntegrity will use the datafile, it means step 4 of persistence failed', function (done) {
var theDb = new Datastore({ filename: 'workspace/it.db' });
if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); }
if (fs.existsSync('workspace/it.db~~')) { fs.unlinkSync('workspace/it.db~~'); }
fs.writeFileSync('workspace/it.db', '{"_id":"0","hello":"world"}', 'utf8');
fs.writeFileSync('workspace/it.db~~', '{"_id":"0","hello":"other"}', 'utf8');
fs.existsSync('workspace/it.db').should.equal(true);
fs.existsSync('workspace/it.db~~').should.equal(true);
theDb.persistence.ensureDatafileIntegrity(function (err) {
fs.existsSync('workspace/it.db~~').should.equal(true);
storage.ensureDatafileIntegrity(theDb.persistence.filename, function (err) {
assert.isNull(err);
fs.existsSync('workspace/it.db').should.equal(true);
fs.existsSync('workspace/it.db~~').should.equal(true);
fs.readFileSync('workspace/it.db', 'utf8').should.equal('{"_id":"0","hello":"world"}');
theDb.loadDatabase(function (err) {

@@ -688,3 +689,3 @@ assert.isNull(err);

});
it('persistCachedDatabase should update the contents of the datafile and leave a clean state', function (done) {

@@ -694,3 +695,3 @@ d.insert({ hello: 'world' }, function () {

docs.length.should.equal(1);
if (fs.existsSync(testDb)) { fs.unlinkSync(testDb); }

@@ -700,3 +701,3 @@ if (fs.existsSync(testDb + '~')) { fs.unlinkSync(testDb + '~'); }

fs.existsSync(testDb).should.equal(false);
fs.writeFileSync(testDb + '~', 'something', 'utf8');

@@ -706,3 +707,3 @@ fs.writeFileSync(testDb + '~~', 'something else', 'utf8');

fs.existsSync(testDb + '~~').should.equal(true);
d.persistence.persistCachedDatabase(function (err) {

@@ -722,3 +723,3 @@ var contents = fs.readFileSync(testDb, 'utf8');

});
it('After a persistCachedDatabase, there should be no temp or old filename', function (done) {

@@ -728,3 +729,3 @@ d.insert({ hello: 'world' }, function () {

docs.length.should.equal(1);
if (fs.existsSync(testDb)) { fs.unlinkSync(testDb); }

@@ -734,3 +735,3 @@ if (fs.existsSync(testDb + '~')) { fs.unlinkSync(testDb + '~'); }

fs.existsSync(testDb).should.equal(false);
fs.writeFileSync(testDb + '~', 'bloup', 'utf8');

@@ -740,3 +741,3 @@ fs.writeFileSync(testDb + '~~', 'blap', 'utf8');

fs.existsSync(testDb + '~~').should.equal(true);
d.persistence.persistCachedDatabase(function (err) {

@@ -756,3 +757,3 @@ var contents = fs.readFileSync(testDb, 'utf8');

});
it('persistCachedDatabase should update the contents of the datafile and leave a clean state even if there is a temp or old datafile', function (done) {

@@ -762,3 +763,3 @@ d.insert({ hello: 'world' }, function () {

docs.length.should.equal(1);
if (fs.existsSync(testDb)) { fs.unlinkSync(testDb); }

@@ -770,3 +771,3 @@ fs.writeFileSync(testDb + '~', 'blabla', 'utf8');

fs.existsSync(testDb + '~~').should.equal(true);
d.persistence.persistCachedDatabase(function (err) {

@@ -786,12 +787,12 @@ var contents = fs.readFileSync(testDb, 'utf8');

});
it('persistCachedDatabase should update the contents of the datafile and leave a clean state even if there is a temp or old datafile', function (done) {
var dbFile = 'workspace/test2.db', theDb;
if (fs.existsSync(dbFile)) { fs.unlinkSync(dbFile); }
if (fs.existsSync(dbFile + '~')) { fs.unlinkSync(dbFile + '~'); }
if (fs.existsSync(dbFile + '~~')) { fs.unlinkSync(dbFile + '~~'); }
theDb = new Datastore({ filename: dbFile });
theDb.loadDatabase(function (err) {

@@ -801,4 +802,4 @@ var contents = fs.readFileSync(dbFile, 'utf8');

fs.existsSync(dbFile).should.equal(true);
fs.existsSync(dbFile + '~').should.equal(false);
fs.existsSync(dbFile + '~~').should.equal(false);
fs.existsSync(dbFile + '~').should.equal(false);
fs.existsSync(dbFile + '~~').should.equal(false);
if (contents != "") {

@@ -813,79 +814,79 @@ throw "Datafile contents not as expected";

var dbFile = 'workspace/test2.db', theDb, theDb2, doc1, doc2;
async.waterfall([
async.apply(Persistence.ensureFileDoesntExist, dbFile)
, async.apply(Persistence.ensureFileDoesntExist, dbFile + '~')
, async.apply(Persistence.ensureFileDoesntExist, dbFile + '~~')
async.apply(storage.ensureFileDoesntExist, dbFile)
, async.apply(storage.ensureFileDoesntExist, dbFile + '~')
, async.apply(storage.ensureFileDoesntExist, dbFile + '~~')
, function (cb) {
theDb = new Datastore({ filename: dbFile });
theDb.loadDatabase(cb);
}
, function (cb) {
theDb.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(0);
return cb();
});
}
, function (cb) {
theDb = new Datastore({ filename: dbFile });
theDb.loadDatabase(cb);
theDb.insert({ a: 'hello' }, function (err, _doc1) {
assert.isNull(err);
doc1 = _doc1;
theDb.insert({ a: 'world' }, function (err, _doc2) {
assert.isNull(err);
doc2 = _doc2;
return cb();
});
});
}
, function (cb) {
theDb.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(0);
return cb();
});
theDb.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(2);
_.find(docs, function (item) { return item._id === doc1._id }).a.should.equal('hello');
_.find(docs, function (item) { return item._id === doc2._id }).a.should.equal('world');
return cb();
});
}
, function (cb) {
theDb.insert({ a: 'hello' }, function (err, _doc1) {
assert.isNull(err);
doc1 = _doc1;
theDb.insert({ a: 'world' }, function (err, _doc2) {
assert.isNull(err);
doc2 = _doc2;
return cb();
});
});
theDb.loadDatabase(cb);
}
, function (cb) {
theDb.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(2);
_.find(docs, function (item) { return item._id === doc1._id }).a.should.equal('hello');
_.find(docs, function (item) { return item._id === doc2._id }).a.should.equal('world');
return cb();
});
}
, function (cb) {
theDb.loadDatabase(cb);
}
, function (cb) { // No change
theDb.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(2);
_.find(docs, function (item) { return item._id === doc1._id }).a.should.equal('hello');
_.find(docs, function (item) { return item._id === doc2._id }).a.should.equal('world');
return cb();
});
theDb.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(2);
_.find(docs, function (item) { return item._id === doc1._id }).a.should.equal('hello');
_.find(docs, function (item) { return item._id === doc2._id }).a.should.equal('world');
return cb();
});
}
, function (cb) {
fs.existsSync(dbFile).should.equal(true);
fs.existsSync(dbFile + '~').should.equal(false);
fs.existsSync(dbFile + '~~').should.equal(false);
return cb();
fs.existsSync(dbFile).should.equal(true);
fs.existsSync(dbFile + '~').should.equal(false);
fs.existsSync(dbFile + '~~').should.equal(false);
return cb();
}
, function (cb) {
theDb2 = new Datastore({ filename: dbFile });
theDb2.loadDatabase(cb);
theDb2 = new Datastore({ filename: dbFile });
theDb2.loadDatabase(cb);
}
, function (cb) { // No change in second db
theDb2.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(2);
_.find(docs, function (item) { return item._id === doc1._id }).a.should.equal('hello');
_.find(docs, function (item) { return item._id === doc2._id }).a.should.equal('world');
return cb();
});
theDb2.find({}, function (err, docs) {
assert.isNull(err);
docs.length.should.equal(2);
_.find(docs, function (item) { return item._id === doc1._id }).a.should.equal('hello');
_.find(docs, function (item) { return item._id === doc2._id }).a.should.equal('world');
return cb();
});
}
, function (cb) {
fs.existsSync(dbFile).should.equal(true);
fs.existsSync(dbFile + '~').should.equal(false);
fs.existsSync(dbFile + '~~').should.equal(false);
return cb();
fs.existsSync(dbFile).should.equal(true);
fs.existsSync(dbFile + '~').should.equal(false);
fs.existsSync(dbFile + '~~').should.equal(false);
return cb();
}
], done);
});
// This test is a bit complicated since it depends on the time I/O actions take to execute

@@ -897,3 +898,3 @@ // That depends on the machine and the load on the machine when the tests are run

var cp, N = 150000, toWrite = "", i;
// Ensuring the state is clean

@@ -908,13 +909,13 @@ if (fs.existsSync('workspace/lac.db')) { fs.unlinkSync('workspace/lac.db'); }

fs.writeFileSync('workspace/lac.db', toWrite, 'utf8');
// Loading it in a separate process that we will crash before finishing the loadDatabase
cp = child_process.fork('test_lac/loadAndCrash.test')
// Kill the child process when we're at step 3 of persistCachedDatabase (during write to datafile)
setTimeout(function() {
cp.kill('SIGINT');
// If the timing is correct, only the temp datafile contains data
// The datafile was in the middle of being written and is empty
// Let the process crash be finished then load database without a crash, and test we didn't lose data

@@ -925,3 +926,3 @@ setTimeout(function () {

assert.isNull(err);
db.count({}, function (err, n) {

@@ -931,14 +932,14 @@ // Data has not been lost

n.should.equal(150000);
// State is clean, the temp datafile has been erased and the datafile contains all the data
fs.existsSync('workspace/lac.db').should.equal(true);
fs.existsSync('workspace/lac.db~').should.equal(false);
done();
});
});
}, 100);
}, 100);
}, 2000);
});
}); // ==== End of 'Prevent dataloss when persisting data' ====

@@ -948,5 +949,5 @@

describe('ensureFileDoesntExist', function () {
it('Doesnt do anything if file already doesnt exist', function (done) {
Persistence.ensureFileDoesntExist('workspace/nonexisting', function (err) {
storage.ensureFileDoesntExist('workspace/nonexisting', function (err) {
assert.isNull(err);

@@ -961,4 +962,4 @@ fs.existsSync('workspace/nonexisting').should.equal(false);

fs.existsSync('workspace/existing').should.equal(true);
Persistence.ensureFileDoesntExist('workspace/existing', function (err) {
storage.ensureFileDoesntExist('workspace/existing', function (err) {
assert.isNull(err);

@@ -969,3 +970,3 @@ fs.existsSync('workspace/existing').should.equal(false);

});
}); // ==== End of 'ensureFileDoesntExist' ====

@@ -972,0 +973,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

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