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

@seald-io/nedb

Package Overview
Dependencies
Maintainers
3
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@seald-io/nedb - npm Package Compare versions

Comparing version 2.0.0 to 2.0.1

lib/utils.js

41

browser-version/lib/customUtils.js

@@ -10,3 +10,3 @@ /**

*/
function randomBytes (size) {
const randomBytes = size => {
const bytes = new Array(size)

@@ -26,3 +26,3 @@

*/
function byteArrayToBase64 (uint8) {
const byteArrayToBase64 = uint8 => {
const lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

@@ -32,11 +32,7 @@ const extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes

let temp
let length
let i
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
}
const tripletToBase64 = num => lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
// go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
for (let i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])

@@ -47,16 +43,13 @@ output += tripletToBase64(temp)

// pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) {
case 1:
temp = uint8[uint8.length - 1]
output += lookup[temp >> 2]
output += lookup[(temp << 4) & 0x3F]
output += '=='
break
case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += lookup[temp >> 10]
output += lookup[(temp >> 4) & 0x3F]
output += lookup[(temp << 2) & 0x3F]
output += '='
break
if (extraBytes === 1) {
temp = uint8[uint8.length - 1]
output += lookup[temp >> 2]
output += lookup[(temp << 4) & 0x3F]
output += '=='
} else if (extraBytes === 2) {
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += lookup[temp >> 10]
output += lookup[(temp >> 4) & 0x3F]
output += lookup[(temp << 2) & 0x3F]
output += '='
}

@@ -75,6 +68,4 @@

*/
function uid (len) {
return byteArrayToBase64(randomBytes(Math.ceil(Math.max(8, len * 2)))).replace(/[+/]/g, '').slice(0, len)
}
const uid = len => byteArrayToBase64(randomBytes(Math.ceil(Math.max(8, len * 2)))).replace(/[+/]/g, '').slice(0, len)
module.exports.uid = uid

@@ -16,3 +16,3 @@ /**

function exists (filename, cback) {
const exists = (filename, cback) => {
// eslint-disable-next-line node/handle-callback-err

@@ -25,3 +25,3 @@ store.getItem(filename, (err, value) => {

function rename (filename, newFilename, callback) {
const rename = (filename, newFilename, callback) => {
// eslint-disable-next-line node/handle-callback-err

@@ -38,3 +38,3 @@ store.getItem(filename, (err, value) => {

function writeFile (filename, contents, options, callback) {
const writeFile = (filename, contents, options, callback) => {
// Options do not matter in browser setup

@@ -45,3 +45,3 @@ if (typeof options === 'function') { callback = options }

function appendFile (filename, toAppend, options, callback) {
const appendFile = (filename, toAppend, options, callback) => {
// Options do not matter in browser setup

@@ -58,3 +58,3 @@ if (typeof options === 'function') { callback = options }

function readFile (filename, options, callback) {
const readFile = (filename, options, callback) => {
// Options do not matter in browser setup

@@ -66,3 +66,3 @@ if (typeof options === 'function') { callback = options }

function unlink (filename, callback) {
const unlink = (filename, callback) => {
store.removeItem(filename, () => callback())

@@ -72,10 +72,6 @@ }

// Nothing to do, no directories will be used on the browser
function mkdir (dir, options, callback) {
return callback()
}
const mkdir = (dir, options, callback) => callback()
// Nothing to do, no data corruption possible in the brower
function ensureDatafileIntegrity (filename, callback) {
return callback(null)
}
// Nothing to do, no data corruption possible in the browser
const ensureDatafileIntegrity = (filename, callback) => callback(null)

@@ -82,0 +78,0 @@ // Interface

@@ -9,2 +9,10 @@ # Changelog

## [2.0.1] - 2021-05-19
### Changed
- bump `@seald-io/binary-search-tree` to 1.0.2, which does not depend
on `underscore`;
- replace use of `underscore` by pure JS.
## [2.0.0] - 2021-05-18

@@ -17,2 +25,3 @@

### Changed
- Update `homepage` & `repository` fields in the `package.json`

@@ -19,0 +28,0 @@ - New maintainer [seald](https://github.com/seald/) and new package

/**
* Manage access to data, be it to find, update or remove it
*/
const _ = require('underscore')
const model = require('./model.js')

@@ -60,3 +59,2 @@

const res = []
const self = this
let action

@@ -69,19 +67,20 @@

const keepId = this._projection._id !== 0
this._projection = _.omit(this._projection, '_id')
const { _id, ...rest } = this._projection
this._projection = rest
// Check for consistency
const keys = Object.keys(this._projection)
keys.forEach(function (k) {
if (action !== undefined && self._projection[k] !== action) { throw new Error('Can\'t both keep and omit fields except for _id') }
action = self._projection[k]
keys.forEach(k => {
if (action !== undefined && this._projection[k] !== action) throw new Error('Can\'t both keep and omit fields except for _id')
action = this._projection[k]
})
// Do the actual projection
candidates.forEach(function (candidate) {
candidates.forEach(candidate => {
let toPush
if (action === 1) { // pick-type projection
toPush = { $set: {} }
keys.forEach(function (k) {
keys.forEach(k => {
toPush.$set[k] = model.getDotValue(candidate, k)
if (toPush.$set[k] === undefined) { delete toPush.$set[k] }
if (toPush.$set[k] === undefined) delete toPush.$set[k]
})

@@ -91,10 +90,7 @@ toPush = model.modify({}, toPush)

toPush = { $unset: {} }
keys.forEach(function (k) { toPush.$unset[k] = true })
keys.forEach(k => { toPush.$unset[k] = true })
toPush = model.modify(candidate, toPush)
}
if (keepId) {
toPush._id = candidate._id
} else {
delete toPush._id
}
if (keepId) toPush._id = candidate._id
else delete toPush._id
res.push(toPush)

@@ -117,34 +113,26 @@ })

let skipped = 0
const self = this
let error = null
let i
let keys
let key
function callback (error, res) {
if (self.execFn) {
return self.execFn(error, res, _callback)
} else {
return _callback(error, res)
}
const callback = (error, res) => {
if (this.execFn) return this.execFn(error, res, _callback)
else return _callback(error, res)
}
this.db.getCandidates(this.query, function (err, candidates) {
if (err) { return callback(err) }
this.db.getCandidates(this.query, (err, candidates) => {
if (err) return callback(err)
try {
for (i = 0; i < candidates.length; i += 1) {
if (model.match(candidates[i], self.query)) {
for (const candidate of candidates) {
if (model.match(candidate, this.query)) {
// If a sort is defined, wait for the results to be sorted before applying limit and skip
if (!self._sort) {
if (self._skip && self._skip > skipped) {
skipped += 1
} else {
res.push(candidates[i])
if (!this._sort) {
if (this._skip && this._skip > skipped) skipped += 1
else {
res.push(candidate)
added += 1
if (self._limit && self._limit <= added) { break }
if (this._limit && this._limit <= added) break
}
} else {
res.push(candidates[i])
}
} else res.push(candidate)
}

@@ -157,21 +145,15 @@ }

// Apply all sorts
if (self._sort) {
keys = Object.keys(self._sort)
if (this._sort) {
keys = Object.keys(this._sort)
// Sorting
const criteria = []
for (i = 0; i < keys.length; i++) {
key = keys[i]
criteria.push({ key: key, direction: self._sort[key] })
}
res.sort(function (a, b) {
let criterion
let compare
let i
for (i = 0; i < criteria.length; i++) {
criterion = criteria[i]
compare = criterion.direction * model.compareThings(model.getDotValue(a, criterion.key), model.getDotValue(b, criterion.key), self.db.compareStrings)
if (compare !== 0) {
return compare
}
keys.forEach(item => {
key = item
criteria.push({ key: key, direction: this._sort[key] })
})
res.sort((a, b) => {
for (const criterion of criteria) {
const compare = criterion.direction * model.compareThings(model.getDotValue(a, criterion.key), model.getDotValue(b, criterion.key), this.db.compareStrings)
if (compare !== 0) return compare
}

@@ -182,4 +164,4 @@ return 0

// Applying limit and skip
const limit = self._limit || res.length
const skip = self._skip || 0
const limit = this._limit || res.length
const skip = this._skip || 0

@@ -191,3 +173,3 @@ res = res.slice(skip, skip + limit)

try {
res = self.project(res)
res = this.project(res)
} catch (e) {

@@ -194,0 +176,0 @@ error = e

@@ -11,10 +11,8 @@ const crypto = require('crypto')

*/
function uid (len) {
return crypto.randomBytes(Math.ceil(Math.max(8, len * 2)))
.toString('base64')
.replace(/[+/]/g, '')
.slice(0, len)
}
const uid = len => crypto.randomBytes(Math.ceil(Math.max(8, len * 2)))
.toString('base64')
.replace(/[+/]/g, '')
.slice(0, len)
// Interface
module.exports.uid = uid
const { EventEmitter } = require('events')
const util = require('util')
const async = require('async')
const _ = require('underscore')
const Cursor = require('./cursor.js')

@@ -68,3 +67,3 @@ const customUtils = require('./customUtils.js')

this.executor = new Executor()
if (this.inMemoryOnly) { this.executor.ready = true }
if (this.inMemoryOnly) this.executor.ready = true

@@ -81,5 +80,5 @@ // Indexed by field name, dot notation can be used

if (this.autoload) {
this.loadDatabase(options.onload || function (err) {
if (err) { throw err }
})
this.loadDatabase(options.onload || (err => {
if (err) throw err
}))
}

@@ -106,7 +105,5 @@ }

resetIndexes (newData) {
const self = this
Object.keys(this.indexes).forEach(function (i) {
self.indexes[i].reset(newData)
})
for (const index of Object.values(this.indexes)) {
index.reset(newData)
}
}

@@ -118,2 +115,3 @@

* We use an async API for consistency with the rest of the code
* @param {Object} options
* @param {String} options.fieldName

@@ -123,19 +121,14 @@ * @param {Boolean} options.unique

* @param {Number} options.expireAfterSeconds - Optional, if set this index becomes a TTL index (only works on Date fields, not arrays of Date)
* @param {Function} cb Optional callback, signature: err
* @param {Function} callback Optional callback, signature: err
*/
ensureIndex (options, cb) {
let err
const callback = cb || function () {}
options = options || {}
ensureIndex (options = {}, callback = () => {}) {
if (!options.fieldName) {
err = new Error('Cannot create an index without a fieldName')
const err = new Error('Cannot create an index without a fieldName')
err.missingFieldName = true
return callback(err)
}
if (this.indexes[options.fieldName]) { return callback(null) }
if (this.indexes[options.fieldName]) return callback(null)
this.indexes[options.fieldName] = new Index(options)
if (options.expireAfterSeconds !== undefined) { this.ttlIndexes[options.fieldName] = options.expireAfterSeconds } // With this implementation index creation is not necessary to ensure TTL but we stick with MongoDB's API here
if (options.expireAfterSeconds !== undefined) this.ttlIndexes[options.fieldName] = options.expireAfterSeconds // With this implementation index creation is not necessary to ensure TTL but we stick with MongoDB's API here

@@ -150,4 +143,4 @@ try {

// We may want to force all options to be persisted including defaults, not just the ones passed the index creation function
this.persistence.persistNewState([{ $$indexCreated: options }], function (err) {
if (err) { return callback(err) }
this.persistence.persistNewState([{ $$indexCreated: options }], err => {
if (err) return callback(err)
return callback(null)

@@ -160,11 +153,9 @@ })

* @param {String} fieldName
* @param {Function} cb Optional callback, signature: err
* @param {Function} callback Optional callback, signature: err
*/
removeIndex (fieldName, cb) {
const callback = cb || function () {}
removeIndex (fieldName, callback = () => {}) {
delete this.indexes[fieldName]
this.persistence.persistNewState([{ $$indexRemoved: fieldName }], function (err) {
if (err) { return callback(err) }
this.persistence.persistNewState([{ $$indexRemoved: fieldName }], err => {
if (err) return callback(err)
return callback(null)

@@ -178,3 +169,2 @@ })

addToIndexes (doc) {
let i
let failingIndex

@@ -184,3 +174,3 @@ let error

for (i = 0; i < keys.length; i += 1) {
for (let i = 0; i < keys.length; i += 1) {
try {

@@ -197,3 +187,3 @@ this.indexes[keys[i]].insert(doc)

if (error) {
for (i = 0; i < failingIndex; i += 1) {
for (let i = 0; i < failingIndex; i += 1) {
this.indexes[keys[i]].remove(doc)

@@ -210,7 +200,5 @@ }

removeFromIndexes (doc) {
const self = this
Object.keys(this.indexes).forEach(function (i) {
self.indexes[i].remove(doc)
})
for (const index of Object.values(this.indexes)) {
index.remove(doc)
}
}

@@ -224,3 +212,2 @@

updateIndexes (oldDoc, newDoc) {
let i
let failingIndex

@@ -230,3 +217,3 @@ let error

for (i = 0; i < keys.length; i += 1) {
for (let i = 0; i < keys.length; i += 1) {
try {

@@ -243,3 +230,3 @@ this.indexes[keys[i]].update(oldDoc, newDoc)

if (error) {
for (i = 0; i < failingIndex; i += 1) {
for (let i = 0; i < failingIndex; i += 1) {
this.indexes[keys[i]].revertUpdate(oldDoc, newDoc)

@@ -267,3 +254,2 @@ }

const indexNames = Object.keys(this.indexes)
const self = this
let usableQueryKeys

@@ -278,6 +264,6 @@

// STEP 1: get candidates list by checking indexes from most to least frequent usecase
function (cb) {
cb => {
// For a basic match
usableQueryKeys = []
Object.keys(query).forEach(function (k) {
Object.keys(query).forEach(k => {
if (typeof query[k] === 'string' || typeof query[k] === 'number' || typeof query[k] === 'boolean' || util.types.isDate(query[k]) || query[k] === null) {

@@ -287,5 +273,5 @@ usableQueryKeys.push(k)

})
usableQueryKeys = _.intersection(usableQueryKeys, indexNames)
usableQueryKeys = usableQueryKeys.filter(k => indexNames.includes(k))
if (usableQueryKeys.length > 0) {
return cb(null, self.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]]))
return cb(null, this.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]]))
}

@@ -295,3 +281,3 @@

usableQueryKeys = []
Object.keys(query).forEach(function (k) {
Object.keys(query).forEach(k => {
if (query[k] && Object.prototype.hasOwnProperty.call(query[k], '$in')) {

@@ -301,5 +287,5 @@ usableQueryKeys.push(k)

})
usableQueryKeys = _.intersection(usableQueryKeys, indexNames)
usableQueryKeys = usableQueryKeys.filter(k => indexNames.includes(k))
if (usableQueryKeys.length > 0) {
return cb(null, self.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]].$in))
return cb(null, this.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]].$in))
}

@@ -309,3 +295,3 @@

usableQueryKeys = []
Object.keys(query).forEach(function (k) {
Object.keys(query).forEach(k => {
if (query[k] && (Object.prototype.hasOwnProperty.call(query[k], '$lt') || Object.prototype.hasOwnProperty.call(query[k], '$lte') || Object.prototype.hasOwnProperty.call(query[k], '$gt') || Object.prototype.hasOwnProperty.call(query[k], '$gte'))) {

@@ -315,35 +301,36 @@ usableQueryKeys.push(k)

})
usableQueryKeys = _.intersection(usableQueryKeys, indexNames)
usableQueryKeys = usableQueryKeys.filter(k => indexNames.includes(k))
if (usableQueryKeys.length > 0) {
return cb(null, self.indexes[usableQueryKeys[0]].getBetweenBounds(query[usableQueryKeys[0]]))
return cb(null, this.indexes[usableQueryKeys[0]].getBetweenBounds(query[usableQueryKeys[0]]))
}
// By default, return all the DB data
return cb(null, self.getAllData())
return cb(null, this.getAllData())
},
// STEP 2: remove all expired documents
function (docs) {
if (dontExpireStaleDocs) { return callback(null, docs) }
docs => {
if (dontExpireStaleDocs) return callback(null, docs)
const expiredDocsIds = []
const validDocs = []
const ttlIndexesFieldNames = Object.keys(self.ttlIndexes)
const ttlIndexesFieldNames = Object.keys(this.ttlIndexes)
docs.forEach(function (doc) {
docs.forEach(doc => {
let valid = true
ttlIndexesFieldNames.forEach(function (i) {
if (doc[i] !== undefined && util.types.isDate(doc[i]) && Date.now() > doc[i].getTime() + self.ttlIndexes[i] * 1000) {
ttlIndexesFieldNames.forEach(i => {
if (doc[i] !== undefined && util.types.isDate(doc[i]) && Date.now() > doc[i].getTime() + this.ttlIndexes[i] * 1000) {
valid = false
}
})
if (valid) { validDocs.push(doc) } else { expiredDocsIds.push(doc._id) }
if (valid) validDocs.push(doc)
else expiredDocsIds.push(doc._id)
})
async.eachSeries(expiredDocsIds, function (_id, cb) {
self._remove({ _id: _id }, {}, function (err) {
if (err) { return callback(err) }
async.eachSeries(expiredDocsIds, (_id, cb) => {
this._remove({ _id: _id }, {}, err => {
if (err) return callback(err)
return cb()
})
// eslint-disable-next-line node/handle-callback-err
}, function (err) {
}, err => {
// TODO: handle error

@@ -357,8 +344,8 @@ return callback(null, validDocs)

* Insert a new document
* @param {Function} cb Optional callback, signature: err, insertedDoc
* @param {Document} newDoc
* @param {Function} callback Optional callback, signature: err, insertedDoc
*
* @api private Use Datastore.insert which has the same signature
*/
_insert (newDoc, cb) {
const callback = cb || function () {}
_insert (newDoc, callback = () => {}) {
let preparedDoc

@@ -373,4 +360,4 @@

this.persistence.persistNewState(Array.isArray(preparedDoc) ? preparedDoc : [preparedDoc], function (err) {
if (err) { return callback(err) }
this.persistence.persistNewState(Array.isArray(preparedDoc) ? preparedDoc : [preparedDoc], err => {
if (err) return callback(err)
return callback(null, model.deepCopy(preparedDoc))

@@ -384,8 +371,6 @@ })

createNewId () {
let tentativeId = customUtils.uid(16)
let attemptId = customUtils.uid(16)
// Try as many times as needed to get an unused _id. As explained in customUtils, the probability of this ever happening is extremely small, so this is O(1)
if (this.indexes._id.getMatching(tentativeId).length > 0) {
tentativeId = this.createNewId()
}
return tentativeId
if (this.indexes._id.getMatching(attemptId).length > 0) attemptId = this.createNewId()
return attemptId
}

@@ -400,13 +385,12 @@

let preparedDoc
const self = this
if (Array.isArray(newDoc)) {
preparedDoc = []
newDoc.forEach(function (doc) { preparedDoc.push(self.prepareDocumentForInsertion(doc)) })
newDoc.forEach(doc => { preparedDoc.push(this.prepareDocumentForInsertion(doc)) })
} else {
preparedDoc = model.deepCopy(newDoc)
if (preparedDoc._id === undefined) { preparedDoc._id = this.createNewId() }
if (preparedDoc._id === undefined) preparedDoc._id = this.createNewId()
const now = new Date()
if (this.timestampData && preparedDoc.createdAt === undefined) { preparedDoc.createdAt = now }
if (this.timestampData && preparedDoc.updatedAt === undefined) { preparedDoc.updatedAt = now }
if (this.timestampData && preparedDoc.createdAt === undefined) preparedDoc.createdAt = now
if (this.timestampData && preparedDoc.updatedAt === undefined) preparedDoc.updatedAt = now
model.checkObject(preparedDoc)

@@ -423,7 +407,4 @@ }

_insertInCache (preparedDoc) {
if (Array.isArray(preparedDoc)) {
this._insertMultipleDocsInCache(preparedDoc)
} else {
this.addToIndexes(preparedDoc)
}
if (Array.isArray(preparedDoc)) this._insertMultipleDocsInCache(preparedDoc)
else this.addToIndexes(preparedDoc)
}

@@ -437,7 +418,6 @@

_insertMultipleDocsInCache (preparedDocs) {
let i
let failingI
let failingIndex
let error
for (i = 0; i < preparedDocs.length; i += 1) {
for (let i = 0; i < preparedDocs.length; i += 1) {
try {

@@ -447,3 +427,3 @@ this.addToIndexes(preparedDocs[i])

error = e
failingI = i
failingIndex = i
break

@@ -454,3 +434,3 @@ }

if (error) {
for (i = 0; i < failingI; i += 1) {
for (let i = 0; i < failingIndex; i += 1) {
this.removeFromIndexes(preparedDocs[i])

@@ -470,2 +450,3 @@ }

* @param {Object} query MongoDB-style query
* @param {Function} callback Optional callback, signature: err, count
*/

@@ -478,7 +459,4 @@ count (query, callback) {

if (typeof callback === 'function') {
cursor.exec(callback)
} else {
return cursor
}
if (typeof callback === 'function') cursor.exec(callback)
else return cursor
}

@@ -491,26 +469,20 @@

* @param {Object} projection MongoDB-style projection
* @param {Function} callback Optional callback, signature: err, docs
*/
find (query, projection, callback) {
switch (arguments.length) {
case 1:
if (arguments.length === 1) {
projection = {}
// callback is undefined, will return a cursor
} else if (arguments.length === 2) {
if (typeof projection === 'function') {
callback = projection
projection = {}
// callback is undefined, will return a cursor
break
case 2:
if (typeof projection === 'function') {
callback = projection
projection = {}
} // If not assume projection is an object and callback undefined
break
} // If not assume projection is an object and callback undefined
}
const cursor = new Cursor(this, query, function (err, docs, callback) {
const res = []
let i
if (err) { return callback(err) }
for (i = 0; i < docs.length; i += 1) {
res.push(model.deepCopy(docs[i]))
}
const res = docs.map(doc => model.deepCopy(doc))
return callback(null, res)

@@ -520,7 +492,4 @@ })

cursor.projection(projection)
if (typeof callback === 'function') {
cursor.exec(callback)
} else {
return cursor
}
if (typeof callback === 'function') cursor.exec(callback)
else return cursor
}

@@ -532,32 +501,24 @@

* @param {Object} projection MongoDB-style projection
* @param {Function} callback Optional callback, signature: err, doc
*/
findOne (query, projection, callback) {
switch (arguments.length) {
case 1:
if (arguments.length === 1) {
projection = {}
// callback is undefined, will return a cursor
} else if (arguments.length === 2) {
if (typeof projection === 'function') {
callback = projection
projection = {}
// callback is undefined, will return a cursor
break
case 2:
if (typeof projection === 'function') {
callback = projection
projection = {}
} // If not assume projection is an object and callback undefined
break
} // If not assume projection is an object and callback undefined
}
const cursor = new Cursor(this, query, function (err, docs, callback) {
if (err) { return callback(err) }
if (docs.length === 1) {
return callback(null, model.deepCopy(docs[0]))
} else {
return callback(null, null)
}
const cursor = new Cursor(this, query, (err, docs, callback) => {
if (err) return callback(err)
if (docs.length === 1) return callback(null, model.deepCopy(docs[0]))
else return callback(null, null)
})
cursor.projection(projection).limit(1)
if (typeof callback === 'function') {
cursor.exec(callback)
} else {
return cursor
}
if (typeof callback === 'function') cursor.exec(callback)
else return cursor
}

@@ -591,6 +552,2 @@

_update (query, updateQuery, options, cb) {
const self = this
let numReplaced = 0
let i
if (typeof options === 'function') {

@@ -600,3 +557,3 @@ cb = options

}
const callback = cb || function () {}
const callback = cb || (() => {})
const multi = options.multi !== undefined ? options.multi : false

@@ -606,12 +563,11 @@ const upsert = options.upsert !== undefined ? options.upsert : false

async.waterfall([
function (cb) { // If upsert option is set, check whether we need to insert the doc
if (!upsert) { return cb() }
cb => { // If upsert option is set, check whether we need to insert the doc
if (!upsert) return cb()
// Need to use an internal function not tied to the executor to avoid deadlock
const cursor = new Cursor(self, query)
cursor.limit(1)._exec(function (err, docs) {
if (err) { return callback(err) }
if (docs.length === 1) {
return cb()
} else {
const cursor = new Cursor(this, query)
cursor.limit(1)._exec((err, docs) => {
if (err) return callback(err)
if (docs.length === 1) return cb()
else {
let toBeInserted

@@ -633,4 +589,4 @@

return self._insert(toBeInserted, function (err, newDoc) {
if (err) { return callback(err) }
return this._insert(toBeInserted, (err, newDoc) => {
if (err) return callback(err)
return callback(null, 1, newDoc, true)

@@ -641,3 +597,4 @@ })

},
function () { // Perform the update
() => { // Perform the update
let numReplaced = 0
let modifiedDoc

@@ -647,4 +604,4 @@ const modifications = []

self.getCandidates(query, function (err, candidates) {
if (err) { return callback(err) }
this.getCandidates(query, (err, candidates) => {
if (err) return callback(err)

@@ -654,12 +611,12 @@ // Preparing update (if an error is thrown here neither the datafile nor

try {
for (i = 0; i < candidates.length; i += 1) {
if (model.match(candidates[i], query) && (multi || numReplaced === 0)) {
for (const candidate of candidates) {
if (model.match(candidate, query) && (multi || numReplaced === 0)) {
numReplaced += 1
if (self.timestampData) { createdAt = candidates[i].createdAt }
modifiedDoc = model.modify(candidates[i], updateQuery)
if (self.timestampData) {
if (this.timestampData) { createdAt = candidate.createdAt }
modifiedDoc = model.modify(candidate, updateQuery)
if (this.timestampData) {
modifiedDoc.createdAt = createdAt
modifiedDoc.updatedAt = new Date()
}
modifications.push({ oldDoc: candidates[i], newDoc: modifiedDoc })
modifications.push({ oldDoc: candidate, newDoc: modifiedDoc })
}

@@ -673,3 +630,3 @@ }

try {
self.updateIndexes(modifications)
this.updateIndexes(modifications)
} catch (err) {

@@ -680,5 +637,5 @@ return callback(err)

// Update the datafile
const updatedDocs = _.pluck(modifications, 'newDoc')
self.persistence.persistNewState(updatedDocs, function (err) {
if (err) { return callback(err) }
const updatedDocs = modifications.map(x => x.newDoc)
this.persistence.persistNewState(updatedDocs, err => {
if (err) return callback(err)
if (!options.returnUpdatedDocs) {

@@ -688,4 +645,4 @@ return callback(null, numReplaced)

let updatedDocsDC = []
updatedDocs.forEach(function (doc) { updatedDocsDC.push(model.deepCopy(doc)) })
if (!multi) { updatedDocsDC = updatedDocsDC[0] }
updatedDocs.forEach(doc => { updatedDocsDC.push(model.deepCopy(doc)) })
if (!multi) updatedDocsDC = updatedDocsDC[0]
return callback(null, numReplaced, updatedDocsDC)

@@ -713,6 +670,2 @@ }

_remove (query, options, cb) {
const self = this
let numRemoved = 0
const removedDocs = []
if (typeof options === 'function') {

@@ -722,20 +675,24 @@ cb = options

}
const callback = cb || function () {}
const callback = cb || (() => {})
const multi = options.multi !== undefined ? options.multi : false
this.getCandidates(query, true, function (err, candidates) {
if (err) { return callback(err) }
this.getCandidates(query, true, (err, candidates) => {
if (err) return callback(err)
const removedDocs = []
let numRemoved = 0
try {
candidates.forEach(function (d) {
candidates.forEach(d => {
if (model.match(d, query) && (multi || numRemoved === 0)) {
numRemoved += 1
removedDocs.push({ $$deleted: true, _id: d._id })
self.removeFromIndexes(d)
this.removeFromIndexes(d)
}
})
} catch (err) { return callback(err) }
} catch (err) {
return callback(err)
}
self.persistence.persistNewState(removedDocs, function (err) {
if (err) { return callback(err) }
this.persistence.persistNewState(removedDocs, err => {
if (err) return callback(err)
return callback(null, numRemoved)

@@ -742,0 +699,0 @@ })

@@ -12,9 +12,8 @@ /**

// This queue will execute all commands, one-by-one in order
this.queue = async.queue(function (task, cb) {
const newArguments = []
this.queue = async.queue((task, cb) => {
// task.arguments is an array-like object on which adding a new field doesn't work, so we transform it into a real array
for (let i = 0; i < task.arguments.length; i += 1) { newArguments.push(task.arguments[i]) }
const lastArg = task.arguments[task.arguments.length - 1]
const newArguments = Array.from(task.arguments)
const lastArg = newArguments[newArguments.length - 1]
// Always tell the queue task is complete. Execute callback if any was given.

@@ -33,6 +32,6 @@ if (typeof lastArg === 'function') {

// false/undefined/null supplied as callback
newArguments[newArguments.length - 1] = function () { cb() }
newArguments[newArguments.length - 1] = () => { cb() }
} else {
// Nothing supplied as callback
newArguments.push(function () { cb() })
newArguments.push(() => { cb() })
}

@@ -55,7 +54,4 @@

push (task, forceQueuing) {
if (this.ready || forceQueuing) {
this.queue.push(task)
} else {
this.buffer.push(task)
}
if (this.ready || forceQueuing) this.queue.push(task)
else this.buffer.push(task)
}

@@ -68,5 +64,4 @@

processBuffer () {
let i
this.ready = true
for (i = 0; i < this.buffer.length; i += 1) { this.queue.push(this.buffer[i]) }
this.buffer.forEach(task => { this.queue.push(task) })
this.buffer = []

@@ -73,0 +68,0 @@ }

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

const _ = require('underscore')
const util = require('util')
const BinarySearchTree = require('@seald-io/binary-search-tree').BinarySearchTree
const model = require('./model.js')
const { uniq } = require('./utils.js')

@@ -8,5 +9,3 @@ /**

*/
function checkValueEquality (a, b) {
return a === b
}
const checkValueEquality = (a, b) => a === b

@@ -17,7 +16,7 @@ /**

function projectForUnique (elt) {
if (elt === null) { return '$null' }
if (typeof elt === 'string') { return '$string' + elt }
if (typeof elt === 'boolean') { return '$boolean' + elt }
if (typeof elt === 'number') { return '$number' + elt }
if (Array.isArray(elt)) { return '$date' + elt.getTime() }
if (elt === null) return '$null'
if (typeof elt === 'string') return '$string' + elt
if (typeof elt === 'boolean') return '$boolean' + elt
if (typeof elt === 'number') return '$number' + elt
if (util.types.isDate(elt)) return '$date' + elt.getTime()

@@ -54,3 +53,3 @@ return elt // Arrays and objects, will check for pointer equality

if (newData) { this.insert(newData) }
if (newData) this.insert(newData)
}

@@ -65,4 +64,3 @@

let keys
let i
let failingI
let failingIndex
let error

@@ -78,11 +76,10 @@

// We don't index documents that don't contain the field if the index is sparse
if (key === undefined && this.sparse) { return }
if (key === undefined && this.sparse) return
if (!Array.isArray(key)) {
this.tree.insert(key, doc)
} else {
if (!Array.isArray(key)) this.tree.insert(key, doc)
else {
// If an insert fails due to a unique constraint, roll back all inserts before it
keys = _.uniq(key, projectForUnique)
keys = uniq(key, projectForUnique)
for (i = 0; i < keys.length; i += 1) {
for (let i = 0; i < keys.length; i += 1) {
try {

@@ -92,3 +89,3 @@ this.tree.insert(keys[i], doc)

error = e
failingI = i
failingIndex = i
break

@@ -99,3 +96,3 @@ }

if (error) {
for (i = 0; i < failingI; i += 1) {
for (let i = 0; i < failingIndex; i += 1) {
this.tree.delete(keys[i], doc)

@@ -116,7 +113,6 @@ }

insertMultipleDocs (docs) {
let i
let error
let failingI
let failingIndex
for (i = 0; i < docs.length; i += 1) {
for (let i = 0; i < docs.length; i += 1) {
try {

@@ -126,3 +122,3 @@ this.insert(docs[i])

error = e
failingI = i
failingIndex = i
break

@@ -133,3 +129,3 @@ }

if (error) {
for (i = 0; i < failingI; i += 1) {
for (let i = 0; i < failingIndex; i += 1) {
this.remove(docs[i])

@@ -149,6 +145,4 @@ }

remove (doc) {
const self = this
if (Array.isArray(doc)) {
doc.forEach(function (d) { self.remove(d) })
doc.forEach(d => { this.remove(d) })
return

@@ -159,3 +153,3 @@ }

if (key === undefined && this.sparse) { return }
if (key === undefined && this.sparse) return

@@ -165,4 +159,4 @@ if (!Array.isArray(key)) {

} else {
_.uniq(key, projectForUnique).forEach(function (_key) {
self.tree.delete(_key, doc)
uniq(key, projectForUnique).forEach(_key => {
this.tree.delete(_key, doc)
})

@@ -202,11 +196,10 @@ }

updateMultipleDocs (pairs) {
let i
let failingI
let failingIndex
let error
for (i = 0; i < pairs.length; i += 1) {
for (let i = 0; i < pairs.length; i += 1) {
this.remove(pairs[i].oldDoc)
}
for (i = 0; i < pairs.length; i += 1) {
for (let i = 0; i < pairs.length; i += 1) {
try {

@@ -216,3 +209,3 @@ this.insert(pairs[i].newDoc)

error = e
failingI = i
failingIndex = i
break

@@ -224,7 +217,7 @@ }

if (error) {
for (i = 0; i < failingI; i += 1) {
for (let i = 0; i < failingIndex; i += 1) {
this.remove(pairs[i].newDoc)
}
for (i = 0; i < pairs.length; i += 1) {
for (let i = 0; i < pairs.length; i += 1) {
this.insert(pairs[i].oldDoc)

@@ -243,6 +236,5 @@ }

if (!Array.isArray(oldDoc)) {
this.update(newDoc, oldDoc)
} else {
oldDoc.forEach(function (pair) {
if (!Array.isArray(oldDoc)) this.update(newDoc, oldDoc)
else {
oldDoc.forEach(pair => {
revert.push({ oldDoc: pair.newDoc, newDoc: pair.oldDoc })

@@ -260,12 +252,9 @@ })

getMatching (value) {
const self = this
if (!Array.isArray(value)) {
return self.tree.search(value)
} else {
if (!Array.isArray(value)) return this.tree.search(value)
else {
const _res = {}
const res = []
value.forEach(function (v) {
self.getMatching(v).forEach(function (doc) {
value.forEach(v => {
this.getMatching(v).forEach(doc => {
_res[doc._id] = doc

@@ -275,3 +264,3 @@ })

Object.keys(_res).forEach(function (_id) {
Object.keys(_res).forEach(_id => {
res.push(_res[_id])

@@ -301,8 +290,4 @@ })

this.tree.executeOnEveryNode(function (node) {
let i
for (i = 0; i < node.data.length; i += 1) {
res.push(node.data[i])
}
this.tree.executeOnEveryNode(node => {
res.push(...node.data)
})

@@ -309,0 +294,0 @@

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

const util = require('util')
const _ = require('underscore')
const { uniq } = require('./utils.js')
const modifierFunctions = {}

@@ -24,14 +24,14 @@ const lastStepModifierFunctions = {}

*/
function checkKey (k, v) {
if (typeof k === 'number') {
k = k.toString()
}
const checkKey = (k, v) => {
if (typeof k === 'number') k = k.toString()
if (k[0] === '$' && !(k === '$$date' && typeof v === 'number') && !(k === '$$deleted' && v === true) && !(k === '$$indexCreated') && !(k === '$$indexRemoved')) {
throw new Error('Field names cannot begin with the $ character')
}
if (
k[0] === '$' &&
!(k === '$$date' && typeof v === 'number') &&
!(k === '$$deleted' && v === true) &&
!(k === '$$indexCreated') &&
!(k === '$$indexRemoved')
) throw new Error('Field names cannot begin with the $ character')
if (k.indexOf('.') !== -1) {
throw new Error('Field names cannot contain a .')
}
if (k.indexOf('.') !== -1) throw new Error('Field names cannot contain a .')
}

@@ -43,5 +43,5 @@

*/
function checkObject (obj) {
const checkObject = obj => {
if (Array.isArray(obj)) {
obj.forEach(function (o) {
obj.forEach(o => {
checkObject(o)

@@ -52,6 +52,8 @@ })

if (typeof obj === 'object' && obj !== null) {
Object.keys(obj).forEach(function (k) {
checkKey(k, obj[k])
checkObject(obj[k])
})
for (const k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
checkKey(k, obj[k])
checkObject(obj[k])
}
}
}

@@ -68,17 +70,15 @@ }

*/
function serialize (obj) {
const res = JSON.stringify(obj, function (k, v) {
const serialize = obj => {
return JSON.stringify(obj, function (k, v) {
checkKey(k, v)
if (v === undefined) { return undefined }
if (v === null) { return null }
if (v === undefined) return undefined
if (v === null) return null
// Hackish way of checking if object is Date (this way it works between execution contexts in node-webkit).
// We can't use value directly because for dates it is already string in this function (date.toJSON was already called), so we use this
if (typeof this[k].getTime === 'function') { return { $$date: this[k].getTime() } }
if (typeof this[k].getTime === 'function') return { $$date: this[k].getTime() }
return v
})
return res
}

@@ -90,11 +90,14 @@

*/
function deserialize (rawData) {
return JSON.parse(rawData, function (k, v) {
if (k === '$$date') { return new Date(v) }
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) { return v }
if (v && v.$$date) { return v.$$date }
const deserialize = rawData => JSON.parse(rawData, function (k, v) {
if (k === '$$date') return new Date(v)
if (
typeof v === 'string' ||
typeof v === 'number' ||
typeof v === 'boolean' ||
v === null
) return v
if (v && v.$$date) return v.$$date
return v
})
}
return v
})

@@ -107,25 +110,22 @@ /**

function deepCopy (obj, strictKeys) {
let res
if (typeof obj === 'boolean' ||
if (
typeof obj === 'boolean' ||
typeof obj === 'number' ||
typeof obj === 'string' ||
obj === null ||
(util.types.isDate(obj))) {
return obj
}
(util.types.isDate(obj))
) return obj
if (Array.isArray(obj)) {
res = []
obj.forEach(function (o) { res.push(deepCopy(o, strictKeys)) })
return res
}
if (Array.isArray(obj)) return obj.map(o => deepCopy(o, strictKeys))
if (typeof obj === 'object') {
res = {}
Object.keys(obj).forEach(function (k) {
if (!strictKeys || (k[0] !== '$' && k.indexOf('.') === -1)) {
const res = {}
for (const k in obj) {
if (
Object.prototype.hasOwnProperty.call(obj, k) &&
(!strictKeys || (k[0] !== '$' && k.indexOf('.') === -1))
) {
res[k] = deepCopy(obj[k], strictKeys)
}
})
}
return res

@@ -141,10 +141,10 @@ }

*/
function isPrimitiveType (obj) {
return (typeof obj === 'boolean' ||
typeof obj === 'number' ||
typeof obj === 'string' ||
obj === null ||
util.types.isDate(obj) ||
Array.isArray(obj))
}
const isPrimitiveType = obj => (
typeof obj === 'boolean' ||
typeof obj === 'number' ||
typeof obj === 'string' ||
obj === null ||
util.types.isDate(obj) ||
Array.isArray(obj)
)

@@ -156,16 +156,14 @@ /**

*/
function compareNSB (a, b) {
if (a < b) { return -1 }
if (a > b) { return 1 }
const compareNSB = (a, b) => {
if (a < b) return -1
if (a > b) return 1
return 0
}
function compareArrays (a, b) {
let i
let comp
const compareArrays = (a, b) => {
const minLength = Math.min(a.length, b.length)
for (let i = 0; i < minLength; i += 1) {
const comp = compareThings(a[i], b[i])
for (i = 0; i < Math.min(a.length, b.length); i += 1) {
comp = compareThings(a[i], b[i])
if (comp !== 0) { return comp }
if (comp !== 0) return comp
}

@@ -187,34 +185,32 @@

*/
function compareThings (a, b, _compareStrings) {
let comp
let i
const compareThings = (a, b, _compareStrings) => {
const compareStrings = _compareStrings || compareNSB
// undefined
if (a === undefined) { return b === undefined ? 0 : -1 }
if (b === undefined) { return a === undefined ? 0 : 1 }
if (a === undefined) return b === undefined ? 0 : -1
if (b === undefined) return 1 // no need to test if a === undefined
// null
if (a === null) { return b === null ? 0 : -1 }
if (b === null) { return a === null ? 0 : 1 }
if (a === null) return b === null ? 0 : -1
if (b === null) return 1 // no need to test if a === null
// Numbers
if (typeof a === 'number') { return typeof b === 'number' ? compareNSB(a, b) : -1 }
if (typeof b === 'number') { return typeof a === 'number' ? compareNSB(a, b) : 1 }
if (typeof a === 'number') return typeof b === 'number' ? compareNSB(a, b) : -1
if (typeof b === 'number') return typeof a === 'number' ? compareNSB(a, b) : 1
// Strings
if (typeof a === 'string') { return typeof b === 'string' ? compareStrings(a, b) : -1 }
if (typeof b === 'string') { return typeof a === 'string' ? compareStrings(a, b) : 1 }
if (typeof a === 'string') return typeof b === 'string' ? compareStrings(a, b) : -1
if (typeof b === 'string') return typeof a === 'string' ? compareStrings(a, b) : 1
// Booleans
if (typeof a === 'boolean') { return typeof b === 'boolean' ? compareNSB(a, b) : -1 }
if (typeof b === 'boolean') { return typeof a === 'boolean' ? compareNSB(a, b) : 1 }
if (typeof a === 'boolean') return typeof b === 'boolean' ? compareNSB(a, b) : -1
if (typeof b === 'boolean') return typeof a === 'boolean' ? compareNSB(a, b) : 1
// Dates
if (util.types.isDate(a)) { return util.types.isDate(b) ? compareNSB(a.getTime(), b.getTime()) : -1 }
if (util.types.isDate(b)) { return util.types.isDate(a) ? compareNSB(a.getTime(), b.getTime()) : 1 }
if (util.types.isDate(a)) return util.types.isDate(b) ? compareNSB(a.getTime(), b.getTime()) : -1
if (util.types.isDate(b)) return util.types.isDate(a) ? compareNSB(a.getTime(), b.getTime()) : 1
// Arrays (first element is most significant and so on)
if (Array.isArray(a)) { return Array.isArray(b) ? compareArrays(a, b) : -1 }
if (Array.isArray(b)) { return Array.isArray(a) ? compareArrays(a, b) : 1 }
if (Array.isArray(a)) return Array.isArray(b) ? compareArrays(a, b) : -1
if (Array.isArray(b)) return Array.isArray(a) ? compareArrays(a, b) : 1

@@ -225,6 +221,6 @@ // Objects

for (i = 0; i < Math.min(aKeys.length, bKeys.length); i += 1) {
comp = compareThings(a[aKeys[i]], b[bKeys[i]])
for (let i = 0; i < Math.min(aKeys.length, bKeys.length); i += 1) {
const comp = compareThings(a[aKeys[i]], b[bKeys[i]])
if (comp !== 0) { return comp }
if (comp !== 0) return comp
}

@@ -251,3 +247,3 @@

*/
lastStepModifierFunctions.$set = function (obj, field, value) {
lastStepModifierFunctions.$set = (obj, field, value) => {
obj[field] = value

@@ -259,3 +255,3 @@ }

*/
lastStepModifierFunctions.$unset = function (obj, field, value) {
lastStepModifierFunctions.$unset = (obj, field, value) => {
delete obj[field]

@@ -270,25 +266,30 @@ }

*/
lastStepModifierFunctions.$push = function (obj, field, value) {
lastStepModifierFunctions.$push = (obj, field, value) => {
// Create the array if it doesn't exist
if (!Object.prototype.hasOwnProperty.call(obj, field)) { obj[field] = [] }
if (!Object.prototype.hasOwnProperty.call(obj, field)) obj[field] = []
if (!Array.isArray(obj[field])) { throw new Error('Can\'t $push an element on non-array values') }
if (!Array.isArray(obj[field])) throw new Error('Can\'t $push an element on non-array values')
if (value !== null && typeof value === 'object' && value.$slice && value.$each === undefined) {
value.$each = []
}
if (
value !== null &&
typeof value === 'object' &&
value.$slice &&
value.$each === undefined
) value.$each = []
if (value !== null && typeof value === 'object' && value.$each) {
if (Object.keys(value).length >= 3 || (Object.keys(value).length === 2 && value.$slice === undefined)) { throw new Error('Can only use $slice in cunjunction with $each when $push to array') }
if (!Array.isArray(value.$each)) { throw new Error('$each requires an array value') }
if (
Object.keys(value).length >= 3 ||
(Object.keys(value).length === 2 && value.$slice === undefined)
) throw new Error('Can only use $slice in cunjunction with $each when $push to array')
if (!Array.isArray(value.$each)) throw new Error('$each requires an array value')
value.$each.forEach(function (v) {
value.$each.forEach(v => {
obj[field].push(v)
})
if (value.$slice === undefined || typeof value.$slice !== 'number') { return }
if (value.$slice === undefined || typeof value.$slice !== 'number') return
if (value.$slice === 0) {
obj[field] = []
} else {
if (value.$slice === 0) obj[field] = []
else {
let start

@@ -316,22 +317,21 @@ let end

*/
lastStepModifierFunctions.$addToSet = function (obj, field, value) {
let addToSet = true
lastStepModifierFunctions.$addToSet = (obj, field, value) => {
// Create the array if it doesn't exist
if (!Object.prototype.hasOwnProperty.call(obj, field)) { obj[field] = [] }
if (!Array.isArray(obj[field])) { throw new Error('Can\'t $addToSet an element on non-array values') }
if (!Array.isArray(obj[field])) throw new Error('Can\'t $addToSet an element on non-array values')
if (value !== null && typeof value === 'object' && value.$each) {
if (Object.keys(value).length > 1) { throw new Error('Can\'t use another field in conjunction with $each') }
if (!Array.isArray(value.$each)) { throw new Error('$each requires an array value') }
if (Object.keys(value).length > 1) throw new Error('Can\'t use another field in conjunction with $each')
if (!Array.isArray(value.$each)) throw new Error('$each requires an array value')
value.$each.forEach(function (v) {
value.$each.forEach(v => {
lastStepModifierFunctions.$addToSet(obj, field, v)
})
} else {
obj[field].forEach(function (v) {
if (compareThings(v, value) === 0) { addToSet = false }
let addToSet = true
obj[field].forEach(v => {
if (compareThings(v, value) === 0) addToSet = false
})
if (addToSet) { obj[field].push(value) }
if (addToSet) obj[field].push(value)
}

@@ -343,12 +343,9 @@ }

*/
lastStepModifierFunctions.$pop = function (obj, field, value) {
if (!Array.isArray(obj[field])) { throw new Error('Can\'t $pop an element from non-array values') }
if (typeof value !== 'number') { throw new Error(value + ' isn\'t an integer, can\'t use it with $pop') }
if (value === 0) { return }
lastStepModifierFunctions.$pop = (obj, field, value) => {
if (!Array.isArray(obj[field])) throw new Error('Can\'t $pop an element from non-array values')
if (typeof value !== 'number') throw new Error(`${value} isn't an integer, can't use it with $pop`)
if (value === 0) return
if (value > 0) {
obj[field] = obj[field].slice(0, obj[field].length - 1)
} else {
obj[field] = obj[field].slice(1)
}
if (value > 0) obj[field] = obj[field].slice(0, obj[field].length - 1)
else obj[field] = obj[field].slice(1)
}

@@ -359,10 +356,8 @@

*/
lastStepModifierFunctions.$pull = function (obj, field, value) {
if (!Array.isArray(obj[field])) { throw new Error('Can\'t $pull an element from non-array values') }
lastStepModifierFunctions.$pull = (obj, field, value) => {
if (!Array.isArray(obj[field])) throw new Error('Can\'t $pull an element from non-array values')
const arr = obj[field]
for (let i = arr.length - 1; i >= 0; i -= 1) {
if (match(arr[i], value)) {
arr.splice(i, 1)
}
if (match(arr[i], value)) arr.splice(i, 1)
}

@@ -374,14 +369,9 @@ }

*/
lastStepModifierFunctions.$inc = function (obj, field, value) {
if (typeof value !== 'number') { throw new Error(value + ' must be a number') }
lastStepModifierFunctions.$inc = (obj, field, value) => {
if (typeof value !== 'number') throw new Error(`${value} must be a number`)
if (typeof obj[field] !== 'number') {
if (!_.has(obj, field)) {
obj[field] = value
} else {
throw new Error('Don\'t use the $inc modifier on non-number fields')
}
} else {
obj[field] += value
}
if (!Object.prototype.hasOwnProperty.call(obj, field)) obj[field] = value
else throw new Error('Don\'t use the $inc modifier on non-number fields')
} else obj[field] += value
}

@@ -392,8 +382,5 @@

*/
lastStepModifierFunctions.$max = function (obj, field, value) {
if (typeof obj[field] === 'undefined') {
obj[field] = value
} else if (value > obj[field]) {
obj[field] = value
}
lastStepModifierFunctions.$max = (obj, field, value) => {
if (typeof obj[field] === 'undefined') obj[field] = value
else if (value > obj[field]) obj[field] = value
}

@@ -404,24 +391,18 @@

*/
lastStepModifierFunctions.$min = function (obj, field, value) {
if (typeof obj[field] === 'undefined') {
obj[field] = value
} else if (value < obj[field]) {
obj[field] = value
}
lastStepModifierFunctions.$min = (obj, field, value) => {
if (typeof obj[field] === 'undefined') obj[field] = value
else if (value < obj[field]) obj[field] = value
}
// Given its name, create the complete modifier function
function createModifierFunction (modifier) {
return function (obj, field, value) {
const fieldParts = typeof field === 'string' ? field.split('.') : field
const createModifierFunction = modifier => (obj, field, value) => {
const fieldParts = typeof field === 'string' ? field.split('.') : field
if (fieldParts.length === 1) {
lastStepModifierFunctions[modifier](obj, field, value)
} else {
if (obj[fieldParts[0]] === undefined) {
if (modifier === '$unset') { return } // Bad looking specific fix, needs to be generalized modifiers that behave like $unset are implemented
obj[fieldParts[0]] = {}
}
modifierFunctions[modifier](obj[fieldParts[0]], fieldParts.slice(1), value)
if (fieldParts.length === 1) lastStepModifierFunctions[modifier](obj, field, value)
else {
if (obj[fieldParts[0]] === undefined) {
if (modifier === '$unset') return // Bad looking specific fix, needs to be generalized modifiers that behave like $unset are implemented
obj[fieldParts[0]] = {}
}
modifierFunctions[modifier](obj[fieldParts[0]], fieldParts.slice(1), value)
}

@@ -431,3 +412,3 @@ }

// Actually create all modifier functions
Object.keys(lastStepModifierFunctions).forEach(function (modifier) {
Object.keys(lastStepModifierFunctions).forEach(modifier => {
modifierFunctions[modifier] = createModifierFunction(modifier)

@@ -439,14 +420,12 @@ })

*/
function modify (obj, updateQuery) {
const modify = (obj, updateQuery) => {
const keys = Object.keys(updateQuery)
const firstChars = _.map(keys, function (item) { return item[0] })
const dollarFirstChars = _.filter(firstChars, function (c) { return c === '$' })
const firstChars = keys.map(item => item[0])
const dollarFirstChars = firstChars.filter(c => c === '$')
let newDoc
let modifiers
if (keys.indexOf('_id') !== -1 && updateQuery._id !== obj._id) { throw new Error('You cannot change a document\'s _id') }
if (keys.indexOf('_id') !== -1 && updateQuery._id !== obj._id) throw new Error('You cannot change a document\'s _id')
if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) {
throw new Error('You cannot mix modifiers and normal fields')
}
if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) throw new Error('You cannot mix modifiers and normal fields')

@@ -459,15 +438,13 @@ if (dollarFirstChars.length === 0) {

// Apply modifiers
modifiers = _.uniq(keys)
modifiers = uniq(keys)
newDoc = deepCopy(obj)
modifiers.forEach(function (m) {
if (!modifierFunctions[m]) { throw new Error('Unknown modifier ' + m) }
modifiers.forEach(m => {
if (!modifierFunctions[m]) throw new Error(`Unknown modifier ${m}`)
// Can't rely on Object.keys throwing on non objects since ES6
// Not 100% satisfying as non objects can be interpreted as objects but no false negatives so we can live with it
if (typeof updateQuery[m] !== 'object') {
throw new Error('Modifier ' + m + '\'s argument must be an object')
}
if (typeof updateQuery[m] !== 'object') throw new Error(`Modifier ${m}'s argument must be an object`)
const keys = Object.keys(updateQuery[m])
keys.forEach(function (k) {
keys.forEach(k => {
modifierFunctions[m](newDoc, k, updateQuery[m][k])

@@ -481,3 +458,3 @@ })

if (obj._id !== newDoc._id) { throw new Error('You can\'t change a document\'s _id') }
if (obj._id !== newDoc._id) throw new Error('You can\'t change a document\'s _id')
return newDoc

@@ -495,29 +472,19 @@ }

*/
function getDotValue (obj, field) {
const getDotValue = (obj, field) => {
const fieldParts = typeof field === 'string' ? field.split('.') : field
let i
let objs
if (!obj) { return undefined } // field cannot be empty so that means we should return undefined so that nothing can match
if (!obj) return undefined // field cannot be empty so that means we should return undefined so that nothing can match
if (fieldParts.length === 0) { return obj }
if (fieldParts.length === 0) return obj
if (fieldParts.length === 1) { return obj[fieldParts[0]] }
if (fieldParts.length === 1) return obj[fieldParts[0]]
if (Array.isArray(obj[fieldParts[0]])) {
// If the next field is an integer, return only this item of the array
i = parseInt(fieldParts[1], 10)
if (typeof i === 'number' && !isNaN(i)) {
return getDotValue(obj[fieldParts[0]][i], fieldParts.slice(2))
}
const i = parseInt(fieldParts[1], 10)
if (typeof i === 'number' && !isNaN(i)) return getDotValue(obj[fieldParts[0]][i], fieldParts.slice(2))
// Return the array of values
objs = []
for (i = 0; i < obj[fieldParts[0]].length; i += 1) {
objs.push(getDotValue(obj[fieldParts[0]][i], fieldParts.slice(1)))
}
return objs
} else {
return getDotValue(obj[fieldParts[0]], fieldParts.slice(1))
}
return obj[fieldParts[0]].map(el => getDotValue(el, fieldParts.slice(1)))
} else return getDotValue(obj[fieldParts[0]], fieldParts.slice(1))
}

@@ -531,20 +498,29 @@

*/
function areThingsEqual (a, b) {
let aKeys
let bKeys
let i
const areThingsEqual = (a, b) => {
// Strings, booleans, numbers, null
if (a === null || typeof a === 'string' || typeof a === 'boolean' || typeof a === 'number' ||
b === null || typeof b === 'string' || typeof b === 'boolean' || typeof b === 'number') { return a === b }
if (
a === null ||
typeof a === 'string' ||
typeof a === 'boolean' ||
typeof a === 'number' ||
b === null ||
typeof b === 'string' ||
typeof b === 'boolean' ||
typeof b === 'number'
) return a === b
// Dates
if (util.types.isDate(a) || util.types.isDate(b)) { return util.types.isDate(a) && util.types.isDate(b) && a.getTime() === b.getTime() }
if (util.types.isDate(a) || util.types.isDate(b)) return util.types.isDate(a) && util.types.isDate(b) && a.getTime() === b.getTime()
// Arrays (no match since arrays are used as a $in)
// undefined (no match since they mean field doesn't exist and can't be serialized)
if ((!(Array.isArray(a) && Array.isArray(b)) && (Array.isArray(a) || Array.isArray(b))) || a === undefined || b === undefined) { return false }
if (
(!(Array.isArray(a) && Array.isArray(b)) && (Array.isArray(a) || Array.isArray(b))) ||
a === undefined || b === undefined
) return false
// General objects (check for deep equality)
// a and b should be objects at this point
let aKeys
let bKeys
try {

@@ -557,6 +533,6 @@ aKeys = Object.keys(a)

if (aKeys.length !== bKeys.length) { return false }
for (i = 0; i < aKeys.length; i += 1) {
if (bKeys.indexOf(aKeys[i]) === -1) { return false }
if (!areThingsEqual(a[aKeys[i]], b[aKeys[i]])) { return false }
if (aKeys.length !== bKeys.length) return false
for (const el of aKeys) {
if (bKeys.indexOf(el) === -1) return false
if (!areThingsEqual(a[el], b[el])) return false
}

@@ -569,9 +545,13 @@ return true

*/
function areComparable (a, b) {
if (typeof a !== 'string' && typeof a !== 'number' && !util.types.isDate(a) &&
typeof b !== 'string' && typeof b !== 'number' && !util.types.isDate(b)) {
return false
}
const areComparable = (a, b) => {
if (
typeof a !== 'string' &&
typeof a !== 'number' &&
!util.types.isDate(a) &&
typeof b !== 'string' &&
typeof b !== 'number' &&
!util.types.isDate(b)
) return false
if (typeof a !== typeof b) { return false }
if (typeof a !== typeof b) return false

@@ -586,30 +566,17 @@ return true

*/
comparisonFunctions.$lt = function (a, b) {
return areComparable(a, b) && a < b
}
comparisonFunctions.$lt = (a, b) => areComparable(a, b) && a < b
comparisonFunctions.$lte = function (a, b) {
return areComparable(a, b) && a <= b
}
comparisonFunctions.$lte = (a, b) => areComparable(a, b) && a <= b
comparisonFunctions.$gt = function (a, b) {
return areComparable(a, b) && a > b
}
comparisonFunctions.$gt = (a, b) => areComparable(a, b) && a > b
comparisonFunctions.$gte = function (a, b) {
return areComparable(a, b) && a >= b
}
comparisonFunctions.$gte = (a, b) => areComparable(a, b) && a >= b
comparisonFunctions.$ne = function (a, b) {
if (a === undefined) { return true }
return !areThingsEqual(a, b)
}
comparisonFunctions.$ne = (a, b) => a === undefined || !areThingsEqual(a, b)
comparisonFunctions.$in = function (a, b) {
let i
comparisonFunctions.$in = (a, b) => {
if (!Array.isArray(b)) throw new Error('$in operator called with a non-array')
if (!Array.isArray(b)) { throw new Error('$in operator called with a non-array') }
for (i = 0; i < b.length; i += 1) {
if (areThingsEqual(a, b[i])) { return true }
for (const el of b) {
if (areThingsEqual(a, el)) return true
}

@@ -620,4 +587,4 @@

comparisonFunctions.$nin = function (a, b) {
if (!Array.isArray(b)) { throw new Error('$nin operator called with a non-array') }
comparisonFunctions.$nin = (a, b) => {
if (!Array.isArray(b)) throw new Error('$nin operator called with a non-array')

@@ -627,45 +594,32 @@ return !comparisonFunctions.$in(a, b)

comparisonFunctions.$regex = function (a, b) {
if (!util.types.isRegExp(b)) { throw new Error('$regex operator called with non regular expression') }
comparisonFunctions.$regex = (a, b) => {
if (!util.types.isRegExp(b)) throw new Error('$regex operator called with non regular expression')
if (typeof a !== 'string') {
return false
} else {
return b.test(a)
}
if (typeof a !== 'string') return false
else return b.test(a)
}
comparisonFunctions.$exists = function (value, exists) {
if (exists || exists === '') { // This will be true for all values of stat except false, null, undefined and 0
exists = true // That's strange behaviour (we should only use true/false) but that's the way Mongo does it...
} else {
exists = false
}
comparisonFunctions.$exists = (value, exists) => {
// This will be true for all values of stat except false, null, undefined and 0
// That's strange behaviour (we should only use true/false) but that's the way Mongo does it...
if (exists || exists === '') exists = true
else exists = false
if (value === undefined) {
return !exists
} else {
return exists
}
if (value === undefined) return !exists
else return exists
}
// Specific to arrays
comparisonFunctions.$size = function (obj, value) {
if (!Array.isArray(obj)) { return false }
if (value % 1 !== 0) { throw new Error('$size operator called without an integer') }
comparisonFunctions.$size = (obj, value) => {
if (!Array.isArray(obj)) return false
if (value % 1 !== 0) throw new Error('$size operator called without an integer')
return obj.length === value
}
comparisonFunctions.$elemMatch = function (obj, value) {
if (!Array.isArray(obj)) { return false }
let i = obj.length
let result = false // Initialize result
while (i--) {
if (match(obj[i], value)) { // If match for array element, return true
result = true
break
}
}
return result
comparisonFunctions.$elemMatch = (obj, value) => {
if (!Array.isArray(obj)) return false
return obj.some(el => match(el, value))
}
arrayComparisonFunctions.$size = true

@@ -679,9 +633,7 @@ arrayComparisonFunctions.$elemMatch = true

*/
logicalOperators.$or = function (obj, query) {
let i
logicalOperators.$or = (obj, query) => {
if (!Array.isArray(query)) throw new Error('$or operator used without an array')
if (!Array.isArray(query)) { throw new Error('$or operator used without an array') }
for (i = 0; i < query.length; i += 1) {
if (match(obj, query[i])) { return true }
for (let i = 0; i < query.length; i += 1) {
if (match(obj, query[i])) return true
}

@@ -697,9 +649,7 @@

*/
logicalOperators.$and = function (obj, query) {
let i
logicalOperators.$and = (obj, query) => {
if (!Array.isArray(query)) throw new Error('$and operator used without an array')
if (!Array.isArray(query)) { throw new Error('$and operator used without an array') }
for (i = 0; i < query.length; i += 1) {
if (!match(obj, query[i])) { return false }
for (let i = 0; i < query.length; i += 1) {
if (!match(obj, query[i])) return false
}

@@ -715,5 +665,3 @@

*/
logicalOperators.$not = function (obj, query) {
return !match(obj, query)
}
logicalOperators.$not = (obj, query) => !match(obj, query)

@@ -725,7 +673,7 @@ /**

*/
logicalOperators.$where = function (obj, fn) {
if (!_.isFunction(fn)) { throw new Error('$where operator used without a function') }
logicalOperators.$where = (obj, fn) => {
if (typeof fn !== 'function') throw new Error('$where operator used without a function')
const result = fn.call(obj)
if (!_.isBoolean(result)) { throw new Error('$where function must return boolean') }
if (typeof result !== 'boolean') throw new Error('$where function must return boolean')

@@ -740,25 +688,16 @@ return result

*/
function match (obj, query) {
let queryKey
let queryValue
let i
const match = (obj, query) => {
// Primitive query against a primitive type
// This is a bit of a hack since we construct an object with an arbitrary key only to dereference it later
// But I don't have time for a cleaner implementation now
if (isPrimitiveType(obj) || isPrimitiveType(query)) {
return matchQueryPart({ needAKey: obj }, 'needAKey', query)
}
if (isPrimitiveType(obj) || isPrimitiveType(query)) return matchQueryPart({ needAKey: obj }, 'needAKey', query)
// Normal query
const queryKeys = Object.keys(query)
for (i = 0; i < queryKeys.length; i += 1) {
queryKey = queryKeys[i]
queryValue = query[queryKey]
if (queryKey[0] === '$') {
if (!logicalOperators[queryKey]) { throw new Error('Unknown logical operator ' + queryKey) }
if (!logicalOperators[queryKey](obj, queryValue)) { return false }
} else {
if (!matchQueryPart(obj, queryKey, queryValue)) { return false }
for (const queryKey in query) {
if (Object.prototype.hasOwnProperty.call(query, queryKey)) {
const queryValue = query[queryKey]
if (queryKey[0] === '$') {
if (!logicalOperators[queryKey]) throw new Error(`Unknown logical operator ${queryKey}`)
if (!logicalOperators[queryKey](obj, queryValue)) return false
} else if (!matchQueryPart(obj, queryKey, queryValue)) return false
}

@@ -776,6 +715,2 @@ }

const objValue = getDotValue(obj, queryKey)
let i
let keys
let firstChars
let dollarFirstChars

@@ -785,11 +720,8 @@ // Check if the value is an array if we don't force a treatment as value

// If the queryValue is an array, try to perform an exact match
if (Array.isArray(queryValue)) {
return matchQueryPart(obj, queryKey, queryValue, true)
}
if (Array.isArray(queryValue)) return matchQueryPart(obj, queryKey, queryValue, true)
// Check if we are using an array-specific comparison function
if (queryValue !== null && typeof queryValue === 'object' && !util.types.isRegExp(queryValue)) {
keys = Object.keys(queryValue)
for (i = 0; i < keys.length; i += 1) {
if (arrayComparisonFunctions[keys[i]]) { return matchQueryPart(obj, queryKey, queryValue, true) }
for (const key in queryValue) {
if (Object.prototype.hasOwnProperty.call(queryValue, key) && arrayComparisonFunctions[key]) { return matchQueryPart(obj, queryKey, queryValue, true) }
}

@@ -799,4 +731,4 @@ }

// If not, treat it as an array of { obj, query } where there needs to be at least one match
for (i = 0; i < objValue.length; i += 1) {
if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true } // k here could be any string
for (const el of objValue) {
if (matchQueryPart({ k: el }, 'k', queryValue)) return true // k here could be any string
}

@@ -809,16 +741,14 @@ return false

if (queryValue !== null && typeof queryValue === 'object' && !util.types.isRegExp(queryValue) && !Array.isArray(queryValue)) {
keys = Object.keys(queryValue)
firstChars = _.map(keys, function (item) { return item[0] })
dollarFirstChars = _.filter(firstChars, function (c) { return c === '$' })
const keys = Object.keys(queryValue)
const firstChars = keys.map(item => item[0])
const dollarFirstChars = firstChars.filter(c => c === '$')
if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) {
throw new Error('You cannot mix operators and normal fields')
}
if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) throw new Error('You cannot mix operators and normal fields')
// queryValue is an object of this form: { $comparisonOperator1: value1, ... }
if (dollarFirstChars.length > 0) {
for (i = 0; i < keys.length; i += 1) {
if (!comparisonFunctions[keys[i]]) { throw new Error('Unknown comparison function ' + keys[i]) }
for (const key of keys) {
if (!comparisonFunctions[key]) throw new Error(`Unknown comparison function ${key}`)
if (!comparisonFunctions[keys[i]](objValue, queryValue[keys[i]])) { return false }
if (!comparisonFunctions[key](objValue, queryValue[key])) return false
}

@@ -830,9 +760,7 @@ return true

// Using regular expressions with basic querying
if (util.types.isRegExp(queryValue)) { return comparisonFunctions.$regex(objValue, queryValue) }
if (util.types.isRegExp(queryValue)) return comparisonFunctions.$regex(objValue, queryValue)
// queryValue is either a native value or a normal object
// Basic matching is possible
if (!areThingsEqual(objValue, queryValue)) { return false }
return true
return areThingsEqual(objValue, queryValue)
}

@@ -839,0 +767,0 @@

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

constructor (options) {
let i
let j
let randomString
this.db = options.db

@@ -32,18 +28,24 @@ this.inMemoryOnly = this.db.inMemoryOnly

if (!this.inMemoryOnly && this.filename && this.filename.charAt(this.filename.length - 1) === '~') {
throw new Error('The datafile name can\'t end with a ~, which is reserved for crash safe backup files')
}
if (
!this.inMemoryOnly &&
this.filename &&
this.filename.charAt(this.filename.length - 1) === '~'
) throw new Error('The datafile name can\'t end with a ~, which is reserved for crash safe backup files')
// After serialization and before deserialization hooks with some basic sanity checks
if (options.afterSerialization && !options.beforeDeserialization) {
throw new Error('Serialization hook defined but deserialization hook undefined, cautiously refusing to start NeDB to prevent dataloss')
}
if (!options.afterSerialization && options.beforeDeserialization) {
throw new Error('Serialization hook undefined but deserialization hook defined, cautiously refusing to start NeDB to prevent dataloss')
}
this.afterSerialization = options.afterSerialization || function (s) { return s }
this.beforeDeserialization = options.beforeDeserialization || function (s) { return s }
for (i = 1; i < 30; i += 1) {
for (j = 0; j < 10; j += 1) {
randomString = customUtils.uid(i)
if (
options.afterSerialization &&
!options.beforeDeserialization
) throw new Error('Serialization hook defined but deserialization hook undefined, cautiously refusing to start NeDB to prevent dataloss')
if (
!options.afterSerialization &&
options.beforeDeserialization
) throw new Error('Serialization hook undefined but deserialization hook defined, cautiously refusing to start NeDB to prevent dataloss')
this.afterSerialization = options.afterSerialization || (s => s)
this.beforeDeserialization = options.beforeDeserialization || (s => s)
for (let i = 1; i < 30; i += 1) {
for (let j = 0; j < 10; j += 1) {
const randomString = customUtils.uid(i)
if (this.beforeDeserialization(this.afterSerialization(randomString)) !== randomString) {

@@ -72,21 +74,19 @@ throw new Error('beforeDeserialization is not the reverse of afterSerialization, cautiously refusing to start NeDB to prevent dataloss')

* while the data file is append-only so it may grow larger
* @param {Function} cb Optional callback, signature: err
* @param {Function} callback Optional callback, signature: err
*/
persistCachedDatabase (cb) {
const callback = cb || function () {}
persistCachedDatabase (callback = () => {}) {
let toPersist = ''
const self = this
if (this.inMemoryOnly) { return callback(null) }
if (this.inMemoryOnly) return callback(null)
this.db.getAllData().forEach(function (doc) {
toPersist += self.afterSerialization(model.serialize(doc)) + '\n'
this.db.getAllData().forEach(doc => {
toPersist += this.afterSerialization(model.serialize(doc)) + '\n'
})
Object.keys(this.db.indexes).forEach(function (fieldName) {
Object.keys(this.db.indexes).forEach(fieldName => {
if (fieldName !== '_id') { // The special _id index is managed by datastore.js, the others need to be persisted
toPersist += self.afterSerialization(model.serialize({
toPersist += this.afterSerialization(model.serialize({
$$indexCreated: {
fieldName: fieldName,
unique: self.db.indexes[fieldName].unique,
sparse: self.db.indexes[fieldName].sparse
unique: this.db.indexes[fieldName].unique,
sparse: this.db.indexes[fieldName].sparse
}

@@ -97,5 +97,5 @@ })) + '\n'

storage.crashSafeWriteFile(this.filename, toPersist, function (err) {
if (err) { return callback(err) }
self.db.emit('compaction.done')
storage.crashSafeWriteFile(this.filename, toPersist, err => {
if (err) return callback(err)
this.db.emit('compaction.done')
return callback(null)

@@ -117,3 +117,2 @@ })

setAutocompactionInterval (interval) {
const self = this
const minInterval = 5000

@@ -124,4 +123,4 @@ const realInterval = Math.max(interval || 0, minInterval)

this.autocompactionIntervalId = setInterval(function () {
self.compactDatafile()
this.autocompactionIntervalId = setInterval(() => {
this.compactDatafile()
}, realInterval)

@@ -134,3 +133,3 @@ }

stopAutocompaction () {
if (this.autocompactionIntervalId) { clearInterval(this.autocompactionIntervalId) }
if (this.autocompactionIntervalId) clearInterval(this.autocompactionIntervalId)
}

@@ -142,21 +141,17 @@

* @param {Array} newDocs Can be empty if no doc was updated/removed
* @param {Function} cb Optional, signature: err
* @param {Function} callback Optional, signature: err
*/
persistNewState (newDocs, cb) {
const self = this
persistNewState (newDocs, callback = () => {}) {
let toPersist = ''
const callback = cb || function () {}
// In-memory only datastore
if (self.inMemoryOnly) { return callback(null) }
if (this.inMemoryOnly) return callback(null)
newDocs.forEach(function (doc) {
toPersist += self.afterSerialization(model.serialize(doc)) + '\n'
newDocs.forEach(doc => {
toPersist += this.afterSerialization(model.serialize(doc)) + '\n'
})
if (toPersist.length === 0) { return callback(null) }
if (toPersist.length === 0) return callback(null)
storage.appendFile(self.filename, toPersist, 'utf8', function (err) {
return callback(err)
})
storage.appendFile(this.filename, toPersist, 'utf8', err => callback(err))
}

@@ -172,22 +167,13 @@

const tdata = []
let i
const indexes = {}
let corruptItems = -1
for (i = 0; i < data.length; i += 1) {
let doc
for (const datum of data) {
try {
doc = model.deserialize(this.beforeDeserialization(data[i]))
const doc = model.deserialize(this.beforeDeserialization(datum))
if (doc._id) {
if (doc.$$deleted === true) {
delete dataById[doc._id]
} else {
dataById[doc._id] = doc
}
} else if (doc.$$indexCreated && doc.$$indexCreated.fieldName != null) {
indexes[doc.$$indexCreated.fieldName] = doc.$$indexCreated
} else if (typeof doc.$$indexRemoved === 'string') {
delete indexes[doc.$$indexRemoved]
}
if (doc.$$deleted === true) delete dataById[doc._id]
else dataById[doc._id] = doc
} else if (doc.$$indexCreated && doc.$$indexCreated.fieldName != null) indexes[doc.$$indexCreated.fieldName] = doc.$$indexCreated
else if (typeof doc.$$indexRemoved === 'string') delete indexes[doc.$$indexRemoved]
} catch (e) {

@@ -199,9 +185,8 @@ corruptItems += 1

// A bit lenient on corruption
if (data.length > 0 && corruptItems / data.length > this.corruptAlertThreshold) {
throw new Error('More than ' + Math.floor(100 * this.corruptAlertThreshold) + '% of the data file is corrupt, the wrong beforeDeserialization hook may be used. Cautiously refusing to start NeDB to prevent dataloss')
}
if (
data.length > 0 &&
corruptItems / data.length > this.corruptAlertThreshold
) throw new Error(`More than ${Math.floor(100 * this.corruptAlertThreshold)}% of the data file is corrupt, the wrong beforeDeserialization hook may be used. Cautiously refusing to start NeDB to prevent dataloss`)
Object.keys(dataById).forEach(function (k) {
tdata.push(dataById[k])
})
tdata.push(...Object.values(dataById))

@@ -219,26 +204,23 @@ return { data: tdata, indexes: indexes }

* This operation is very quick at startup for a big collection (60ms for ~10k docs)
* @param {Function} cb Optional callback, signature: err
* @param {Function} callback Optional callback, signature: err
*/
loadDatabase (cb) {
const callback = cb || function () {}
const self = this
loadDatabase (callback = () => {}) {
this.db.resetIndexes()
self.db.resetIndexes()
// In-memory only datastore
if (self.inMemoryOnly) { return callback(null) }
if (this.inMemoryOnly) return callback(null)
async.waterfall([
function (cb) {
cb => {
// eslint-disable-next-line node/handle-callback-err
Persistence.ensureDirectoryExists(path.dirname(self.filename), function (err) {
Persistence.ensureDirectoryExists(path.dirname(this.filename), err => {
// TODO: handle error
// eslint-disable-next-line node/handle-callback-err
storage.ensureDatafileIntegrity(self.filename, function (err) {
storage.ensureDatafileIntegrity(this.filename, err => {
// TODO: handle error
storage.readFile(self.filename, 'utf8', function (err, rawData) {
if (err) { return cb(err) }
storage.readFile(this.filename, 'utf8', (err, rawData) => {
if (err) return cb(err)
let treatedData
try {
treatedData = self.treatRawData(rawData)
treatedData = this.treatRawData(rawData)
} catch (e) {

@@ -249,4 +231,4 @@ return cb(e)

// Recreate all indexes in the datafile
Object.keys(treatedData.indexes).forEach(function (key) {
self.db.indexes[key] = new Index(treatedData.indexes[key])
Object.keys(treatedData.indexes).forEach(key => {
this.db.indexes[key] = new Index(treatedData.indexes[key])
})

@@ -256,9 +238,9 @@

try {
self.db.resetIndexes(treatedData.data)
this.db.resetIndexes(treatedData.data)
} catch (e) {
self.db.resetIndexes() // Rollback any index which didn't fail
this.db.resetIndexes() // Rollback any index which didn't fail
return cb(e)
}
self.db.persistence.persistCachedDatabase(cb)
this.db.persistence.persistCachedDatabase(cb)
})

@@ -268,6 +250,6 @@ })

}
], function (err) {
if (err) { return callback(err) }
], err => {
if (err) return callback(err)
self.db.executor.processBuffer()
this.db.executor.processBuffer()
return callback(null)

@@ -281,5 +263,3 @@ })

*/
static ensureDirectoryExists (dir, cb) {
const callback = cb || function () {}
static ensureDirectoryExists (dir, callback = () => {}) {
storage.mkdir(dir, { recursive: true }, err => { callback(err) })

@@ -295,22 +275,15 @@ }

switch (process.platform) {
case 'win32':
case 'win64':
home = process.env.LOCALAPPDATA || process.env.APPDATA
if (!home) { throw new Error('Couldn\'t find the base application data folder') }
home = path.join(home, appName)
break
case 'darwin':
home = process.env.HOME
if (!home) { throw new Error('Couldn\'t find the base application data directory') }
home = path.join(home, 'Library', 'Application Support', appName)
break
case 'linux':
home = process.env.HOME
if (!home) { throw new Error('Couldn\'t find the base application data directory') }
home = path.join(home, '.config', appName)
break
default:
throw new Error('Can\'t use the Node Webkit relative path for platform ' + process.platform)
}
if (process.platform === 'win32' || process.platform === 'win64') {
home = process.env.LOCALAPPDATA || process.env.APPDATA
if (!home) throw new Error('Couldn\'t find the base application data folder')
home = path.join(home, appName)
} else if (process.platform === 'darwin') {
home = process.env.HOME
if (!home) throw new Error('Couldn\'t find the base application data directory')
home = path.join(home, 'Library', 'Application Support', appName)
} else if (process.platform === 'linux') {
home = process.env.HOME
if (!home) throw new Error('Couldn\'t find the base application data directory')
home = path.join(home, '.config', appName)
} else throw new Error(`Can't use the Node Webkit relative path for platform ${process.platform}`)

@@ -317,0 +290,0 @@ return path.join(home, 'nedb-data', relativeFilename)

@@ -26,7 +26,7 @@ /**

*/
storage.ensureFileDoesntExist = function (file, callback) {
storage.exists(file, function (exists) {
if (!exists) { return callback(null) }
storage.ensureFileDoesntExist = (file, callback) => {
storage.exists(file, exists => {
if (!exists) return callback(null)
storage.unlink(file, function (err) { return callback(err) })
storage.unlink(file, err => callback(err))
})

@@ -41,3 +41,3 @@ }

*/
storage.flushToStorage = function (options, callback) {
storage.flushToStorage = (options, callback) => {
let filename

@@ -55,8 +55,8 @@ let flags

// except in the very rare event of the first time database is loaded and a crash happens
if (flags === 'r' && (process.platform === 'win32' || process.platform === 'win64')) { return callback(null) }
if (flags === 'r' && (process.platform === 'win32' || process.platform === 'win64')) return callback(null)
fs.open(filename, flags, function (err, fd) {
if (err) { return callback(err) }
fs.fsync(fd, function (errFS) {
fs.close(fd, function (errC) {
fs.open(filename, flags, (err, fd) => {
if (err) return callback(err)
fs.fsync(fd, errFS => {
fs.close(fd, errC => {
if (errFS || errC) {

@@ -79,6 +79,5 @@ const e = new Error('Failed to flush to storage')

* @param {String} data
* @param {Function} cb Optional callback, signature: err
* @param {Function} callback Optional callback, signature: err
*/
storage.crashSafeWriteFile = function (filename, data, cb) {
const callback = cb || function () {}
storage.crashSafeWriteFile = (filename, data, callback = () => {}) => {
const tempFilename = filename + '~'

@@ -88,20 +87,17 @@

async.apply(storage.flushToStorage, { filename: path.dirname(filename), isDir: true }),
function (cb) {
storage.exists(filename, function (exists) {
if (exists) {
storage.flushToStorage(filename, function (err) { return cb(err) })
} else {
return cb()
}
cb => {
storage.exists(filename, exists => {
if (exists) storage.flushToStorage(filename, err => cb(err))
else return cb()
})
},
function (cb) {
storage.writeFile(tempFilename, data, function (err) { return cb(err) })
cb => {
storage.writeFile(tempFilename, data, err => cb(err))
},
async.apply(storage.flushToStorage, tempFilename),
function (cb) {
storage.rename(tempFilename, filename, function (err) { return cb(err) })
cb => {
storage.rename(tempFilename, filename, err => cb(err))
},
async.apply(storage.flushToStorage, { filename: path.dirname(filename), isDir: true })
], function (err) { return callback(err) })
], err => callback(err))
}

@@ -114,17 +110,15 @@

*/
storage.ensureDatafileIntegrity = function (filename, callback) {
storage.ensureDatafileIntegrity = (filename, callback) => {
const tempFilename = filename + '~'
storage.exists(filename, function (filenameExists) {
storage.exists(filename, filenameExists => {
// Write was successful
if (filenameExists) { return callback(null) }
if (filenameExists) return callback(null)
storage.exists(tempFilename, function (oldFilenameExists) {
storage.exists(tempFilename, oldFilenameExists => {
// New database
if (!oldFilenameExists) {
return storage.writeFile(filename, '', 'utf8', function (err) { callback(err) })
}
if (!oldFilenameExists) return storage.writeFile(filename, '', 'utf8', err => { callback(err) })
// Write failed, use old version
storage.rename(tempFilename, filename, function (err) { return callback(err) })
storage.rename(tempFilename, filename, err => callback(err))
})

@@ -131,0 +125,0 @@ })

{
"name": "@seald-io/nedb",
"version": "2.0.0",
"version": "2.0.1",
"files": [

@@ -37,6 +37,5 @@ "lib/**/*.js",

"dependencies": {
"@seald-io/binary-search-tree": "^1.0.0",
"@seald-io/binary-search-tree": "^1.0.2",
"async": "0.2.10",
"localforage": "^1.9.0",
"underscore": "^1.13.1"
"localforage": "^1.9.0"
},

@@ -43,0 +42,0 @@ "devDependencies": {

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

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

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