Compare commits

...

11 Commits

Author SHA1 Message Date
Timothée Rebours 68279ccdf0 add version to changelog 11 months ago
Timothée Rebours 7b4b6856e1 4.0.4 11 months ago
Timothée Rebours a2469d14e9 add thanks in changelog 11 months ago
Timothée Rebours 6ed0db96d7 update Changelog 11 months ago
tex0l 1eeda8f1a6
Merge pull request #44 from RobMayer/update-types 11 months ago
tex0l bc10dded1a
Merge pull request #51 from seald/feat/clean-native-node-modules-imports 11 months ago
tex0l 60929bb9fc Fix typo lib/storage.js 11 months ago
Timothée Rebours 45aee76fea add mention that polyfills of util and events are still necessary 11 months ago
Timothée Rebours ff79ebfee2 add changelog 11 months ago
Timothée Rebours c9640889a0 remove 'path' imports 11 months ago
Rob Mayer 43d6285831 added explicit types to update query 1 year ago
  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,10 +6,12 @@ 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).
## Unreleased ## [4.0.4] - 2024-01-11
### Fixed ### Fixed
- Explicitly import `buffer` [#34](https://github.com/seald/nedb/pull/34). - Explicitly import `buffer` [#34](https://github.com/seald/nedb/pull/34), thanks [maxdaniel98](https://github.com/maxdaniel98).
- 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
@ -76,7 +78,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,6 +838,10 @@ 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,7 +2,6 @@
* 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')
@ -46,7 +45,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((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(filename), function () { callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(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,6 +138,17 @@ 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
@ -176,3 +187,5 @@ 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,6 +253,17 @@ 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
@ -280,3 +291,5 @@ 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>( update<T extends Schema, O extends Nedb.UpdateOptions>(
query: any, query: any,
updateQuery: any, updateQuery: any,
options?: Nedb.UpdateOptions, options?: O,
callback?: ( callback?: (
err: Error | null, err: Error | null,
numberOfUpdated: number, numberOfUpdated: number,
affectedDocuments: Document<T> | Document<T>[] | null, affectedDocuments: O['returnUpdatedDocs'] extends true ? O['multi'] extends true ? Document<T>[] | null : Document<T> | null : null,
upsert: boolean | null upsert: boolean | null
) => void ) => void
): void; ): void;
updateAsync<T extends Schema>( updateAsync<T extends Schema, O extends Nedb.UpdateOptions>(
query: any, query: any,
updateQuery: any, updateQuery: any,
options?: Nedb.UpdateOptions options?: O
): Promise<{ ): Promise<{
numAffected: number; numAffected: number;
affectedDocuments: Document<T> | Document<T>[] | null; affectedDocuments: O['returnUpdatedDocs'] extends true ? O['multi'] extends true ? Document<T>[] | null : Document<T> | null : null;
upsert: boolean; upsert: boolean;
}>; }>;

@ -1,4 +1,3 @@
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')
@ -308,7 +307,7 @@ class Persistence {
// In-memory only datastore // In-memory only datastore
if (this.inMemoryOnly) return if (this.inMemoryOnly) return
await Persistence.ensureDirectoryExistsAsync(path.dirname(this.filename), this.modes.dirMode) await Persistence.ensureParentDirectoryExistsAsync(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
@ -372,12 +371,8 @@ class Persistence {
* @return {Promise<void>} * @return {Promise<void>}
* @private * @private
*/ */
static async ensureDirectoryExistsAsync (dir, mode = DEFAULT_DIR_MODE) { static async ensureParentDirectoryExistsAsync (dir, mode = DEFAULT_DIR_MODE) {
const parsedDir = path.parse(path.resolve(dir)) return storage.ensureParentDirectoryExistsAsync(dir, mode)
// 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/Node Webkit version. * This version is the Node.js 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,6 +271,22 @@ 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
@ -297,3 +313,5 @@ 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.3", "version": "4.0.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@seald-io/nedb", "name": "@seald-io/nedb",
"version": "4.0.3", "version": "4.0.4",
"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.3", "version": "4.0.4",
"files": [ "files": [
"lib/**/*.js", "lib/**/*.js",
"browser-version/**/*.js", "browser-version/**/*.js",

@ -2,7 +2,6 @@
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')
@ -15,7 +14,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.ensureDirectoryExistsAsync(path.dirname(testDb)) await Persistence.ensureParentDirectoryExistsAsync(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,7 +2,6 @@
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')
@ -22,7 +21,7 @@ describe('Cursor', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () { callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(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,7 +1,6 @@
/* 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')
@ -17,7 +16,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.ensureDirectoryExistsAsync(path.dirname(testDb)) await Persistence.ensureParentDirectoryExistsAsync(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,7 +2,6 @@
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')
@ -23,7 +22,7 @@ describe('Database', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () { callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(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,7 +2,6 @@
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')
@ -53,7 +52,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.ensureDirectoryExistsAsync(path.dirname(testDb)) await Persistence.ensureParentDirectoryExistsAsync(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,7 +2,6 @@
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')
@ -154,7 +153,7 @@ describe('Executor', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () { callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(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.ensureDirectoryExistsAsync(path.dirname(testDb)) await Persistence.ensureParentDirectoryExistsAsync(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.ensureDirectoryExistsAsync(path.dirname(testDb), 0o700) await Persistence.ensureParentDirectoryExistsAsync(testDb, 0o700)
assert.equal(await getMode(path.dirname(testDb)), 0o700) assert.equal(await getMode(path.dirname(testDb)), 0o700)
}) })

@ -2,7 +2,6 @@
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')
@ -27,7 +26,7 @@ describe('Persistence', function () {
waterfall([ waterfall([
function (cb) { function (cb) {
callbackify((dirname) => Persistence.ensureDirectoryExistsAsync(dirname))(path.dirname(testDb), function () { callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(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