Comparing version 1.3.0 to 1.4.0
/** | ||
* 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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1234196
50
28124
5
638
39
5
+ Addedlocalforage@^1.3.0
+ Addedimmediate@3.0.6(transitive)
+ Addedlie@3.1.1(transitive)
+ Addedlocalforage@1.10.0(transitive)