Compare commits

..

No commits in common. '68279ccdf09fd3064ce5e02e0f3c30d75d8e2cfa' and 'd4c096b07ade20e89d6c4f965309519a35f76da2' have entirely different histories.

  1. 10
      CHANGELOG.md
  2. 4
      README.md
  3. 3
      benchmarks/commonUtilities.js
  4. 13
      browser-version/lib/storage.browser.js
  5. 13
      browser-version/lib/storage.react-native.js
  6. 12
      index.d.ts
  7. 11
      lib/persistence.js
  8. 20
      lib/storage.js
  9. 4
      package-lock.json
  10. 2
      package.json
  11. 3
      test/cursor.async.test.js
  12. 3
      test/cursor.test.js
  13. 3
      test/db.async.test.js
  14. 3
      test/db.test.js
  15. 3
      test/executor.async.test.js
  16. 3
      test/executor.test.js
  17. 4
      test/persistence.async.test.js
  18. 3
      test/persistence.test.js

@ -6,12 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [4.0.4] - 2024-01-11 ## Unreleased
### Fixed ### Fixed
- Explicitly import `buffer` [#34](https://github.com/seald/nedb/pull/34), thanks [maxdaniel98](https://github.com/maxdaniel98). - Explicitly import `buffer` [#34](https://github.com/seald/nedb/pull/34).
- Fix `Cursor`'s typings [#45](https://github.com/seald/nedb/issues/45). - Fix `Cursor`'s typings [#45](https://github.com/seald/nedb/issues/45)
- Removes unnecessary uses of the native `path` module for the browser and React-Native version by replacing the internal `Persistance.ensureDirectoryExistsAsync` static method with `Persistance.ensureParentDirectoryExistsAsync` so that any `path` functions are used only in Node.js where it is necessary, as it is not necessary for the browser and React-Native [#51](https://github.com/seald/nedb/pull/51).
- Explicit return/callback type for update based on options [#44](https://github.com/seald/nedb/pull/44), thanks [RobMayer](https://github.com/RobMayer).
## [4.0.3] - 2023-12-13 ## [4.0.3] - 2023-12-13
### Fixed ### Fixed
@ -78,7 +76,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `Persistence#persistCachedDatabase` replaced with `Persistence#persistCachedDatabaseAsync`; - `Persistence#persistCachedDatabase` replaced with `Persistence#persistCachedDatabaseAsync`;
- `Persistence#persistNewState` replaced with `Persistence#persistNewStateAsync`; - `Persistence#persistNewState` replaced with `Persistence#persistNewStateAsync`;
- `Persistence#treatRawStream` replaced with `Persistence#treatRawStreamAsync`; - `Persistence#treatRawStream` replaced with `Persistence#treatRawStreamAsync`;
- `Persistence.ensureDirectoryExists` replaced with `Persistence.ensureDirectoryExistsAsync`; - `Persistence.ensureDirectoryExists` replaced with `Persistence#ensureDirectoryExistsAsync`;
- Cursor: - Cursor:
- `Cursor#_exec` replaced with `Cursor#_execAsync`; - `Cursor#_exec` replaced with `Cursor#_execAsync`;
- `Cursor#project` replaced with `Cursor#_project`; - `Cursor#project` replaced with `Cursor#_project`;

@ -838,10 +838,6 @@ This is done for:
the repo because it is unmaintained). It isn't used in the browser nor the repo because it is unmaintained). It isn't used in the browser nor
react-native versions, therefore it is shimmed with an empty object. react-native versions, therefore it is shimmed with an empty object.
However, the `browser` and `react-native` versions rely on node native modules and therefore must be polyfilled:
- `util` with https://github.com/browserify/node-util.
- `events` with https://github.com/browserify/events.
## Performance ## Performance
### Speed ### Speed

@ -2,6 +2,7 @@
* Functions that are used in several benchmark tests * Functions that are used in several benchmark tests
*/ */
const fs = require('fs') const fs = require('fs')
const path = require('path')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
const Persistence = require('../lib/persistence') const Persistence = require('../lib/persistence')
const { callbackify } = require('util') const { callbackify } = require('util')
@ -45,7 +46,7 @@ module.exports.getConfiguration = function (benchDb) {
* Ensure the workspace stat and the db datafile is empty * Ensure the workspace stat and the db datafile is empty
*/ */
module.exports.prepareDb = function (filename, cb) { module.exports.prepareDb = function (filename, cb) {
callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(filename, function () { callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(filename), function () {
fs.access(filename, fs.constants.FS_OK, function (err) { fs.access(filename, fs.constants.FS_OK, function (err) {
if (!err) { if (!err) {
fs.unlink(filename, cb) fs.unlink(filename, cb)

@ -138,17 +138,6 @@ const unlinkAsync = async filename => {
*/ */
const mkdirAsync = (path, options) => Promise.resolve() const mkdirAsync = (path, options) => Promise.resolve()
/**
* Shim for {@link module:storage.ensureParentDirectoryExistsAsync}, nothing to do, no directories will be used on the browser.
* @function
* @param {string} file
* @param {number} [mode]
* @return {Promise<void|string>}
* @alias module:storageBrowser.ensureParentDirectoryExistsAsync
* @async
*/
const ensureParentDirectoryExistsAsync = async (file, mode) => Promise.resolve()
/** /**
* Shim for {@link module:storage.ensureDatafileIntegrityAsync}, nothing to do, no data corruption possible in the browser. * Shim for {@link module:storage.ensureDatafileIntegrityAsync}, nothing to do, no data corruption possible in the browser.
* @param {string} filename * @param {string} filename
@ -187,5 +176,3 @@ module.exports.unlinkAsync = unlinkAsync
module.exports.mkdirAsync = mkdirAsync module.exports.mkdirAsync = mkdirAsync
module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync
module.exports.ensureParentDirectoryExistsAsync = ensureParentDirectoryExistsAsync

@ -253,17 +253,6 @@ const crashSafeWriteFileLinesAsync = async (filename, lines) => {
*/ */
const crashSafeWriteFileLines = callbackify(crashSafeWriteFileLinesAsync) const crashSafeWriteFileLines = callbackify(crashSafeWriteFileLinesAsync)
/**
* Shim for {@link module:storage.ensureParentDirectoryExistsAsync}, nothing to do, no directories will be used on the browser.
* @function
* @param {string} file
* @param {number} [mode]
* @return {Promise<void|string>}
* @alias module:storageBrowser.ensureParentDirectoryExistsAsync
* @async
*/
const ensureParentDirectoryExistsAsync = async (file, mode) => Promise.resolve()
// Interface // Interface
module.exports.exists = exists module.exports.exists = exists
module.exports.existsAsync = existsAsync module.exports.existsAsync = existsAsync
@ -291,5 +280,3 @@ module.exports.mkdirAsync = mkdirAsync
module.exports.ensureDatafileIntegrity = ensureDatafileIntegrity module.exports.ensureDatafileIntegrity = ensureDatafileIntegrity
module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync
module.exports.ensureParentDirectoryExistsAsync = ensureParentDirectoryExistsAsync

12
index.d.ts vendored

@ -105,25 +105,25 @@ declare class Nedb<Schema = Record<string, any>> extends EventEmitter {
projection?: any projection?: any
): Nedb.Cursor<T>; ): Nedb.Cursor<T>;
update<T extends Schema, O extends Nedb.UpdateOptions>( update<T extends Schema>(
query: any, query: any,
updateQuery: any, updateQuery: any,
options?: O, options?: Nedb.UpdateOptions,
callback?: ( callback?: (
err: Error | null, err: Error | null,
numberOfUpdated: number, numberOfUpdated: number,
affectedDocuments: O['returnUpdatedDocs'] extends true ? O['multi'] extends true ? Document<T>[] | null : Document<T> | null : null, affectedDocuments: Document<T> | Document<T>[] | null,
upsert: boolean | null upsert: boolean | null
) => void ) => void
): void; ): void;
updateAsync<T extends Schema, O extends Nedb.UpdateOptions>( updateAsync<T extends Schema>(
query: any, query: any,
updateQuery: any, updateQuery: any,
options?: O options?: Nedb.UpdateOptions
): Promise<{ ): Promise<{
numAffected: number; numAffected: number;
affectedDocuments: O['returnUpdatedDocs'] extends true ? O['multi'] extends true ? Document<T>[] | null : Document<T> | null : null; affectedDocuments: Document<T> | Document<T>[] | null;
upsert: boolean; upsert: boolean;
}>; }>;

@ -1,3 +1,4 @@
const path = require('path')
const { deprecate } = require('util') const { deprecate } = require('util')
const byline = require('./byline') const byline = require('./byline')
const customUtils = require('./customUtils.js') const customUtils = require('./customUtils.js')
@ -307,7 +308,7 @@ class Persistence {
// In-memory only datastore // In-memory only datastore
if (this.inMemoryOnly) return if (this.inMemoryOnly) return
await Persistence.ensureParentDirectoryExistsAsync(this.filename, this.modes.dirMode) await Persistence.ensureDirectoryExistsAsync(path.dirname(this.filename), this.modes.dirMode)
await storage.ensureDatafileIntegrityAsync(this.filename, this.modes.fileMode) await storage.ensureDatafileIntegrityAsync(this.filename, this.modes.fileMode)
let treatedData let treatedData
@ -371,8 +372,12 @@ class Persistence {
* @return {Promise<void>} * @return {Promise<void>}
* @private * @private
*/ */
static async ensureParentDirectoryExistsAsync (dir, mode = DEFAULT_DIR_MODE) { static async ensureDirectoryExistsAsync (dir, mode = DEFAULT_DIR_MODE) {
return storage.ensureParentDirectoryExistsAsync(dir, mode) const parsedDir = path.parse(path.resolve(dir))
// this is because on Windows mkdir throws a permission error when called on the root directory of a volume
if (process.platform !== 'win32' || parsedDir.dir !== parsedDir.root || parsedDir.base !== '') {
await storage.mkdirAsync(dir, { recursive: true, mode })
}
} }
} }

@ -1,6 +1,6 @@
/** /**
* Way data is stored for this database. * Way data is stored for this database.
* This version is the Node.js version. * This version is the Node.js/Node Webkit version.
* It's essentially fs, mkdirp and crash safe write and read functions. * It's essentially fs, mkdirp and crash safe write and read functions.
* *
* @see module:storageBrowser * @see module:storageBrowser
@ -271,22 +271,6 @@ const ensureDatafileIntegrityAsync = async (filename, mode = DEFAULT_FILE_MODE)
else await renameAsync(tempFilename, filename) else await renameAsync(tempFilename, filename)
} }
/**
* Check if a file's parent directory exists and create it on the fly if it is not the case.
* @param {string} filename
* @param {number} mode
* @return {Promise<void>}
* @private
*/
const ensureParentDirectoryExistsAsync = async (filename, mode) => {
const dir = path.dirname(filename)
const parsedDir = path.parse(path.resolve(dir))
// this is because on Windows mkdir throws a permission error when called on the root directory of a volume
if (process.platform !== 'win32' || parsedDir.dir !== parsedDir.root || parsedDir.base !== '') {
await mkdirAsync(dir, { recursive: true, mode })
}
}
// Interface // Interface
module.exports.existsAsync = existsAsync module.exports.existsAsync = existsAsync
@ -313,5 +297,3 @@ module.exports.flushToStorageAsync = flushToStorageAsync
module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync
module.exports.ensureFileDoesntExistAsync = ensureFileDoesntExistAsync module.exports.ensureFileDoesntExistAsync = ensureFileDoesntExistAsync
module.exports.ensureParentDirectoryExistsAsync = ensureParentDirectoryExistsAsync

4
package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "@seald-io/nedb", "name": "@seald-io/nedb",
"version": "4.0.4", "version": "4.0.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@seald-io/nedb", "name": "@seald-io/nedb",
"version": "4.0.4", "version": "4.0.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@seald-io/binary-search-tree": "^1.0.3", "@seald-io/binary-search-tree": "^1.0.3",

@ -1,6 +1,6 @@
{ {
"name": "@seald-io/nedb", "name": "@seald-io/nedb",
"version": "4.0.4", "version": "4.0.3",
"files": [ "files": [
"lib/**/*.js", "lib/**/*.js",
"browser-version/**/*.js", "browser-version/**/*.js",

@ -2,6 +2,7 @@
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const { promises: fs } = require('fs') const { promises: fs } = require('fs')
const assert = require('assert').strict const assert = require('assert').strict
const path = require('path')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
const Persistence = require('../lib/persistence') const Persistence = require('../lib/persistence')
const Cursor = require('../lib/cursor') const Cursor = require('../lib/cursor')
@ -14,7 +15,7 @@ describe('Cursor Async', function () {
d = new Datastore({ filename: testDb }) d = new Datastore({ filename: testDb })
assert.equal(d.filename, testDb) assert.equal(d.filename, testDb)
assert.equal(d.inMemoryOnly, false) assert.equal(d.inMemoryOnly, false)
await Persistence.ensureParentDirectoryExistsAsync(testDb) await Persistence.ensureDirectoryExistsAsync(path.dirname(testDb))
if (await exists(testDb)) await fs.unlink(testDb) if (await exists(testDb)) await fs.unlink(testDb)
await d.loadDatabaseAsync() await d.loadDatabaseAsync()
assert.equal(d.getAllData().length, 0) assert.equal(d.getAllData().length, 0)

@ -2,6 +2,7 @@
const chai = require('chai') const chai = require('chai')
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const fs = require('fs') const fs = require('fs')
const path = require('path')
const { each, waterfall } = require('./utils.test.js') const { each, waterfall } = require('./utils.test.js')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
const Persistence = require('../lib/persistence') const Persistence = require('../lib/persistence')
@ -21,7 +22,7 @@ describe('Cursor', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(testDb, function () { callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () {
fs.access(testDb, fs.constants.F_OK, function (err) { fs.access(testDb, fs.constants.F_OK, function (err) {
if (!err) { if (!err) {
fs.unlink(testDb, cb) fs.unlink(testDb, cb)

@ -1,6 +1,7 @@
/* eslint-env mocha */ /* eslint-env mocha */
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const { promises: fs } = require('fs') const { promises: fs } = require('fs')
const path = require('path')
const assert = require('assert').strict const assert = require('assert').strict
const model = require('../lib/model') const model = require('../lib/model')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
@ -16,7 +17,7 @@ describe('Database async', function () {
d = new Datastore({ filename: testDb }) d = new Datastore({ filename: testDb })
assert.equal(d.filename, testDb) assert.equal(d.filename, testDb)
assert.equal(d.inMemoryOnly, false) assert.equal(d.inMemoryOnly, false)
await Persistence.ensureParentDirectoryExistsAsync(testDb) await Persistence.ensureDirectoryExistsAsync(path.dirname(testDb))
if (await exists(testDb)) await fs.unlink(testDb) if (await exists(testDb)) await fs.unlink(testDb)
await d.loadDatabaseAsync() await d.loadDatabaseAsync()
assert.equal(d.getAllData().length, 0) assert.equal(d.getAllData().length, 0)

@ -2,6 +2,7 @@
const chai = require('chai') const chai = require('chai')
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const fs = require('fs') const fs = require('fs')
const path = require('path')
const { apply, each, waterfall } = require('./utils.test.js') const { apply, each, waterfall } = require('./utils.test.js')
const model = require('../lib/model') const model = require('../lib/model')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
@ -22,7 +23,7 @@ describe('Database', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(testDb, function () { callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () {
fs.access(testDb, fs.constants.FS_OK, function (err) { fs.access(testDb, fs.constants.FS_OK, function (err) {
if (!err) { if (!err) {
fs.unlink(testDb, cb) fs.unlink(testDb, cb)

@ -2,6 +2,7 @@
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const { promises: fs } = require('fs') const { promises: fs } = require('fs')
const assert = require('assert').strict const assert = require('assert').strict
const path = require('path')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
const Persistence = require('../lib/persistence') const Persistence = require('../lib/persistence')
const { exists } = require('./utils.test.js') const { exists } = require('./utils.test.js')
@ -52,7 +53,7 @@ describe('Executor async', function () {
d = new Datastore({ filename: testDb }) d = new Datastore({ filename: testDb })
assert.equal(d.filename, testDb) assert.equal(d.filename, testDb)
assert.equal(d.inMemoryOnly, false) assert.equal(d.inMemoryOnly, false)
await Persistence.ensureParentDirectoryExistsAsync(testDb) await Persistence.ensureDirectoryExistsAsync(path.dirname(testDb))
if (await exists(testDb)) await fs.unlink(testDb) if (await exists(testDb)) await fs.unlink(testDb)
await d.loadDatabaseAsync() await d.loadDatabaseAsync()
assert.equal(d.getAllData().length, 0) assert.equal(d.getAllData().length, 0)

@ -2,6 +2,7 @@
const chai = require('chai') const chai = require('chai')
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const fs = require('fs') const fs = require('fs')
const path = require('path')
const { waterfall } = require('./utils.test.js') const { waterfall } = require('./utils.test.js')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
const Persistence = require('../lib/persistence') const Persistence = require('../lib/persistence')
@ -153,7 +154,7 @@ describe('Executor', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(testDb, function () { callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () {
fs.access(testDb, fs.constants.F_OK, function (err) { fs.access(testDb, fs.constants.F_OK, function (err) {
if (!err) { if (!err) {
fs.unlink(testDb, cb) fs.unlink(testDb, cb)

@ -22,7 +22,7 @@ describe('Persistence async', function () {
d = new Datastore({ filename: testDb }) d = new Datastore({ filename: testDb })
assert.equal(d.filename, testDb) assert.equal(d.filename, testDb)
assert.equal(d.inMemoryOnly, false) assert.equal(d.inMemoryOnly, false)
await Persistence.ensureParentDirectoryExistsAsync(testDb) await Persistence.ensureDirectoryExistsAsync(path.dirname(testDb))
if (await exists(testDb)) await fs.unlink(testDb) if (await exists(testDb)) await fs.unlink(testDb)
await d.loadDatabaseAsync() await d.loadDatabaseAsync()
assert.equal(d.getAllData().length, 0) assert.equal(d.getAllData().length, 0)
@ -1062,7 +1062,7 @@ describe('permissions', function () {
}) })
it('ensureDirectoryExists forwards mode argument', async () => { it('ensureDirectoryExists forwards mode argument', async () => {
await Persistence.ensureParentDirectoryExistsAsync(testDb, 0o700) await Persistence.ensureDirectoryExistsAsync(path.dirname(testDb), 0o700)
assert.equal(await getMode(path.dirname(testDb)), 0o700) assert.equal(await getMode(path.dirname(testDb)), 0o700)
}) })

@ -2,6 +2,7 @@
const chai = require('chai') const chai = require('chai')
const testDb = 'workspace/test.db' const testDb = 'workspace/test.db'
const fs = require('fs') const fs = require('fs')
const path = require('path')
const { apply, waterfall } = require('./utils.test.js') const { apply, waterfall } = require('./utils.test.js')
const model = require('../lib/model') const model = require('../lib/model')
const Datastore = require('../lib/datastore') const Datastore = require('../lib/datastore')
@ -26,7 +27,7 @@ describe('Persistence', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(testDb, function () { callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () {
fs.access(testDb, fs.constants.FS_OK, function (err) { fs.access(testDb, fs.constants.FS_OK, function (err) {
if (!err) { if (!err) {
fs.unlink(testDb, cb) fs.unlink(testDb, cb)

Loading…
Cancel
Save