remove deprecated features, remove 'compaction.done' event, add oncompaction callback, include 'callbackify' polyfill for the browser and use it instead of the 'util' module moved to devDependencies

feat/remove-native-modules-imports
Timothée Rebours 11 months ago
parent 68279ccdf0
commit 08c8076ae9
  1. 89
      browser-version/lib/customUtils.js
  2. 2
      browser-version/lib/storage.react-native.js
  3. 2
      lib/cursor.js
  4. 3
      lib/customUtils.js
  5. 85
      lib/datastore.js
  6. 68
      lib/persistence.js
  7. 24
      package-lock.json
  8. 6
      package.json
  9. 14
      test/db.async.test.js
  10. 18
      test/db.test.js
  11. 14
      test/persistence.async.test.js
  12. 6
      test/persistence.test.js
  13. 74
      webpack.config.js

@ -74,3 +74,92 @@ const byteArrayToBase64 = uint8 => {
const uid = len => 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 module.exports.uid = uid
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors ||
function getOwnPropertyDescriptors (obj) {
const keys = Object.keys(obj)
const descriptors = {}
for (let i = 0; i < keys.length; i++) {
descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i])
}
return descriptors
}
function callbackifyOnRejected (reason, cb) {
// `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
// Because `null` is a special error value in callbacks which means "no error
// occurred", we error-wrap so the callback consumer can distinguish between
// "the promise rejected with null" or "the promise fulfilled with undefined".
if (!reason) {
const newReason = new Error('Promise was rejected with a falsy value')
newReason.reason = reason
reason = newReason
}
return cb(reason)
}
/**
* Wraps a function with a callback, converting it into a callback-style function.
*
* Adapted from https://github.com/browserify/node-util, but without its dependency in `process`
*
* @param {function} original - The original function to be callbackified.
* @returns {function} - The callbackified function.
* @throws {TypeError} - If the "original" argument is not a function or if the last argument is not a function.
*/
function callbackify (original) {
if (typeof original !== 'function') {
throw new TypeError('The "original" argument must be of type Function')
}
// We DO NOT return the promise as it gives the user a false sense that
// the promise is actually somehow related to the callback's execution
// and that the callback throwing will reject the promise.
function callbackified () {
const args = []
for (let i = 0; i < arguments.length; i++) {
args.push(arguments[i])
}
const maybeCb = args.pop()
if (typeof maybeCb !== 'function') {
throw new TypeError('The last argument must be of type Function')
}
const self = this
const cb = function () {
return maybeCb.apply(self, arguments)
}
// In true node style we process the callback on `nextTick` with all the
// implications (stack, `uncaughtException`, `async_hooks`), done with setTimeout in browser.
original.apply(this, args)
.then(function (ret) { setTimeout(cb.bind(null, null, ret)) },
function (rej) { setTimeout(callbackifyOnRejected.bind(null, rej, cb)) })
}
Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original))
Object.defineProperties(callbackified,
getOwnPropertyDescriptors(original))
return callbackified
}
module.exports.callbackify = callbackify

@ -9,7 +9,7 @@
*/ */
const AsyncStorage = require('@react-native-async-storage/async-storage').default const AsyncStorage = require('@react-native-async-storage/async-storage').default
const { callbackify } = require('util') const { callbackify } = require('./customUtils')
/** /**
* Async version of {@link module:storageReactNative.exists}. * Async version of {@link module:storageReactNative.exists}.

@ -1,5 +1,5 @@
const model = require('./model.js') const model = require('./model.js')
const { callbackify } = require('util') const { callbackify } = require('./customUtils.js')
/** /**
* Has a callback * Has a callback

@ -5,6 +5,7 @@
* @private * @private
*/ */
const crypto = require('crypto') const crypto = require('crypto')
const { callbackify } = require('util')
/** /**
* Return a random alphanumerical string of length len * Return a random alphanumerical string of length len
@ -24,3 +25,5 @@ const uid = len => crypto.randomBytes(Math.ceil(Math.max(8, len * 2)))
// Interface // Interface
module.exports.uid = uid module.exports.uid = uid
module.exports.callbackify = callbackify

@ -1,7 +1,5 @@
const { EventEmitter } = require('events')
const { callbackify, deprecate } = require('util')
const Cursor = require('./cursor.js') const Cursor = require('./cursor.js')
const customUtils = require('./customUtils.js') const { uid, callbackify } = require('./customUtils.js')
const Executor = require('./executor.js') const Executor = require('./executor.js')
const Index = require('./indexes.js') const Index = require('./indexes.js')
const model = require('./model.js') const model = require('./model.js')
@ -133,19 +131,12 @@ const { isDate, pick, filterIndexNames } = require('./utils.js')
* @return {string} * @return {string}
*/ */
/**
* @external EventEmitter
* @see http://nodejs.org/api/events.html
*/
/** /**
* @class * @class
* @classdesc The `Datastore` class is the main class of NeDB. * @classdesc The `Datastore` class is the main class of NeDB.
* @extends external:EventEmitter
* @emits Datastore#event:"compaction.done"
* @typicalname NeDB * @typicalname NeDB
*/ */
class Datastore extends EventEmitter { class Datastore {
/** /**
* Create a new collection, either persistent or in-memory. * Create a new collection, either persistent or in-memory.
* *
@ -157,9 +148,7 @@ class Datastore extends EventEmitter {
* Also, if loading fails, all commands registered to the {@link Datastore#executor} afterwards will not be executed. * Also, if loading fails, all commands registered to the {@link Datastore#executor} afterwards will not be executed.
* They will be registered and executed, in sequence, only after a successful loading. * They will be registered and executed, in sequence, only after a successful loading.
* *
* @param {object|string} options Can be an object or a string. If options is a string, the behavior is the same as in * @param {object} options
* v0.6: it will be interpreted as `options.filename`. **Giving a string is deprecated, and will be removed in the
* next major version.**
* @param {string} [options.filename = null] Path to the file where the data is persisted. If left blank, the datastore is * @param {string} [options.filename = null] Path to the file where the data is persisted. If left blank, the datastore is
* automatically considered in-memory only. It cannot end with a `~` which is used in the temporary files NeDB uses to * automatically considered in-memory only. It cannot end with a `~` which is used in the temporary files NeDB uses to
* perform crash-safe writes. Not used if `options.inMemoryOnly` is `true`. * perform crash-safe writes. Not used if `options.inMemoryOnly` is `true`.
@ -177,6 +166,9 @@ class Datastore extends EventEmitter {
* @param {NoParamCallback} [options.onload] If you use autoloading, this is the handler called after the `loadDatabase`. It * @param {NoParamCallback} [options.onload] If you use autoloading, this is the handler called after the `loadDatabase`. It
* takes one `error` argument. If you use autoloading without specifying this handler, and an error happens during * takes one `error` argument. If you use autoloading without specifying this handler, and an error happens during
* load, an error will be thrown. * load, an error will be thrown.
* @param {NoParamCallback} [options.oncompaction] Callback called when a database compaction operation is finished,
* whether it succeeded or failed. If it failed, the first parameter is the Error object that was thrown,
* otherwise it is null.
* @param {serializationHook} [options.beforeDeserialization] Hook you can use to transform data after it was serialized and * @param {serializationHook} [options.beforeDeserialization] Hook you can use to transform data after it was serialized and
* before it is written to disk. Can be used for example to encrypt data before writing database to disk. This * before it is written to disk. Can be used for example to encrypt data before writing database to disk. This
* function takes a string as parameter (one line of an NeDB data file) and outputs the transformed string, **which * function takes a string as parameter (one line of an NeDB data file) and outputs the transformed string, **which
@ -197,38 +189,29 @@ class Datastore extends EventEmitter {
* might be CPU-intensive * might be CPU-intensive
*/ */
constructor (options) { constructor (options) {
super() if (options && typeof options !== 'object') throw new Error('\'options\' must be an object')
let filename options = options || {}
// Retrocompatibility with v0.6 and before const filename = options.filename
if (typeof options === 'string') { /**
deprecate(() => { * Determines if the `Datastore` keeps data in-memory, or if it saves it in storage. Is not read after
filename = options * instanciation.
this.inMemoryOnly = false // Default * @type {boolean}
}, '@seald-io/nedb: Giving a string to the Datastore constructor is deprecated and will be removed in the next major version. Please use an options object with an argument \'filename\'.')() * @protected
} else { */
options = options || {} this.inMemoryOnly = options.inMemoryOnly || false
filename = options.filename /**
/** * Determines if the `Datastore` should autoload the database upon instantiation. Is not read after instanciation.
* Determines if the `Datastore` keeps data in-memory, or if it saves it in storage. Is not read after * @type {boolean}
* instanciation. * @protected
* @type {boolean} */
* @protected this.autoload = options.autoload || false
*/ /**
this.inMemoryOnly = options.inMemoryOnly || false * Determines if the `Datastore` should add `createdAt` and `updatedAt` fields automatically if not set by the user.
/** * @type {boolean}
* Determines if the `Datastore` should autoload the database upon instantiation. Is not read after instanciation. * @protected
* @type {boolean} */
* @protected this.timestampData = options.timestampData || false
*/
this.autoload = options.autoload || false
/**
* Determines if the `Datastore` should add `createdAt` and `updatedAt` fields automatically if not set by the user.
* @type {boolean}
* @protected
*/
this.timestampData = options.timestampData || false
}
// Determine whether in memory or persistent // Determine whether in memory or persistent
if (!filename || typeof filename !== 'string' || filename.length === 0) { if (!filename || typeof filename !== 'string' || filename.length === 0) {
@ -314,6 +297,14 @@ class Datastore extends EventEmitter {
else throw err else throw err
}) })
} else this.autoloadPromise = null } else this.autoloadPromise = null
/**
* Callback called when compaction has occurred
* @protected
* @type {NoParamCallback|null}
*/
this.oncompaction = options.oncompaction || null
/** /**
* Interval if {@link Datastore#setAutocompactionInterval} was called. * Interval if {@link Datastore#setAutocompactionInterval} was called.
* @private * @private
@ -709,7 +700,7 @@ class Datastore extends EventEmitter {
* @private * @private
*/ */
_createNewId () { _createNewId () {
let attemptId = customUtils.uid(16) let attemptId = 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) // 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(attemptId).length > 0) attemptId = this._createNewId() if (this.indexes._id.getMatching(attemptId).length > 0) attemptId = this._createNewId()
return attemptId return attemptId

@ -1,4 +1,3 @@
const { deprecate } = require('util')
const byline = require('./byline') const byline = require('./byline')
const customUtils = require('./customUtils.js') const customUtils = require('./customUtils.js')
const Index = require('./indexes.js') const Index = require('./indexes.js')
@ -95,53 +94,32 @@ class Persistence {
* @private * @private
*/ */
async persistCachedDatabaseAsync () { async persistCachedDatabaseAsync () {
const lines = [] try {
const lines = []
if (this.inMemoryOnly) return
this.db.getAllData().forEach(doc => {
lines.push(this.afterSerialization(model.serialize(doc)))
})
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
lines.push(this.afterSerialization(model.serialize({
$$indexCreated: {
fieldName: this.db.indexes[fieldName].fieldName,
unique: this.db.indexes[fieldName].unique,
sparse: this.db.indexes[fieldName].sparse
}
})))
}
})
await storage.crashSafeWriteFileLinesAsync(this.filename, lines, this.modes)
this.db.emit('compaction.done')
}
/** if (this.inMemoryOnly) return
* @see Datastore#compactDatafile
* @deprecated
* @param {NoParamCallback} [callback = () => {}]
* @see Persistence#compactDatafileAsync
*/
compactDatafile (callback) {
deprecate(_callback => this.db.compactDatafile(_callback), '@seald-io/nedb: calling Datastore#persistence#compactDatafile is deprecated, please use Datastore#compactDatafile, it will be removed in the next major version.')(callback)
}
/** this.db.getAllData().forEach(doc => {
* @see Datastore#setAutocompactionInterval lines.push(this.afterSerialization(model.serialize(doc)))
* @deprecated })
*/ Object.keys(this.db.indexes).forEach(fieldName => {
setAutocompactionInterval (interval) { if (fieldName !== '_id') { // The special _id index is managed by datastore.js, the others need to be persisted
deprecate(_interval => this.db.setAutocompactionInterval(_interval), '@seald-io/nedb: calling Datastore#persistence#setAutocompactionInterval is deprecated, please use Datastore#setAutocompactionInterval, it will be removed in the next major version.')(interval) lines.push(this.afterSerialization(model.serialize({
} $$indexCreated: {
fieldName: this.db.indexes[fieldName].fieldName,
unique: this.db.indexes[fieldName].unique,
sparse: this.db.indexes[fieldName].sparse
}
})))
}
})
/** await storage.crashSafeWriteFileLinesAsync(this.filename, lines, this.modes)
* @see Datastore#stopAutocompaction if (typeof this.db.oncompaction === 'function') this.db.oncompaction(null)
* @deprecated } catch (error) {
*/ if (typeof this.db.oncompaction === 'function') this.db.oncompaction(error)
stopAutocompaction () { throw error
deprecate(() => this.db.stopAutocompaction(), '@seald-io/nedb: calling Datastore#persistence#stopAutocompaction is deprecated, please use Datastore#stopAutocompaction, it will be removed in the next major version.')() }
} }
/** /**

24
package-lock.json generated

@ -10,8 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@seald-io/binary-search-tree": "^1.0.3", "@seald-io/binary-search-tree": "^1.0.3",
"localforage": "^1.9.0", "localforage": "^1.9.0"
"util": "^0.12.4"
}, },
"devDependencies": { "devDependencies": {
"@react-native-async-storage/async-storage": "^1.17.11", "@react-native-async-storage/async-storage": "^1.17.11",
@ -41,6 +40,7 @@
"timers-browserify": "^2.0.12", "timers-browserify": "^2.0.12",
"ts-jest": "^27.1.5", "ts-jest": "^27.1.5",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"util": "^0.12.5",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-cli": "^5.0.1", "webpack-cli": "^5.0.1",
"xvfb-maybe": "^0.2.1" "xvfb-maybe": "^0.2.1"
@ -4700,6 +4700,7 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -5217,6 +5218,7 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"dependencies": { "dependencies": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2" "get-intrinsic": "^1.0.2"
@ -7943,6 +7945,7 @@
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"dependencies": { "dependencies": {
"is-callable": "^1.1.3" "is-callable": "^1.1.3"
} }
@ -8037,7 +8040,8 @@
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
}, },
"node_modules/function.prototype.name": { "node_modules/function.prototype.name": {
"version": "1.1.5", "version": "1.1.5",
@ -8097,6 +8101,7 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
"dev": true,
"dependencies": { "dependencies": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
@ -8230,6 +8235,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"dependencies": { "dependencies": {
"get-intrinsic": "^1.1.3" "get-intrinsic": "^1.1.3"
}, },
@ -8274,6 +8280,7 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": { "dependencies": {
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
}, },
@ -8327,6 +8334,7 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -8338,6 +8346,7 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"dev": true,
"dependencies": { "dependencies": {
"has-symbols": "^1.0.2" "has-symbols": "^1.0.2"
}, },
@ -8670,7 +8679,8 @@
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
}, },
"node_modules/internal-slot": { "node_modules/internal-slot": {
"version": "1.0.4", "version": "1.0.4",
@ -8726,6 +8736,7 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"dev": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0" "has-tostringtag": "^1.0.0"
@ -8807,6 +8818,7 @@
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -8919,6 +8931,7 @@
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
"dev": true,
"dependencies": { "dependencies": {
"has-tostringtag": "^1.0.0" "has-tostringtag": "^1.0.0"
}, },
@ -9096,6 +9109,7 @@
"version": "1.1.10", "version": "1.1.10",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
"integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
"dev": true,
"dependencies": { "dependencies": {
"available-typed-arrays": "^1.0.5", "available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2", "call-bind": "^1.0.2",
@ -15707,6 +15721,7 @@
"version": "0.12.5", "version": "0.12.5",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
"dev": true,
"dependencies": { "dependencies": {
"inherits": "^2.0.3", "inherits": "^2.0.3",
"is-arguments": "^1.0.4", "is-arguments": "^1.0.4",
@ -16075,6 +16090,7 @@
"version": "1.1.9", "version": "1.1.9",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
"integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
"dev": true,
"dependencies": { "dependencies": {
"available-typed-arrays": "^1.0.5", "available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2", "call-bind": "^1.0.2",

@ -46,8 +46,7 @@
}, },
"dependencies": { "dependencies": {
"@seald-io/binary-search-tree": "^1.0.3", "@seald-io/binary-search-tree": "^1.0.3",
"localforage": "^1.9.0", "localforage": "^1.9.0"
"util": "^0.12.4"
}, },
"devDependencies": { "devDependencies": {
"@react-native-async-storage/async-storage": "^1.17.11", "@react-native-async-storage/async-storage": "^1.17.11",
@ -77,6 +76,7 @@
"timers-browserify": "^2.0.12", "timers-browserify": "^2.0.12",
"ts-jest": "^27.1.5", "ts-jest": "^27.1.5",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"util": "^0.12.5",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-cli": "^5.0.1", "webpack-cli": "^5.0.1",
"xvfb-maybe": "^0.2.1" "xvfb-maybe": "^0.2.1"
@ -84,7 +84,7 @@
"scripts": { "scripts": {
"lint": "standard", "lint": "standard",
"test": "mocha --reporter spec --timeout 10000", "test": "mocha --reporter spec --timeout 10000",
"build:browser": "webpack && webpack --optimization-minimize", "build:browser": "webpack --config-name Nedb && webpack --config-name Nedb --env minimize && webpack --config-name testUtils --env minimize",
"pretest:browser": "npm run build:browser", "pretest:browser": "npm run build:browser",
"test:browser": "xvfb-maybe karma start karma.conf.local.js", "test:browser": "xvfb-maybe karma start karma.conf.local.js",
"test:react-native": "jest test/react-native", "test:react-native": "jest test/react-native",

@ -22,20 +22,6 @@ describe('Database async', function () {
assert.equal(d.getAllData().length, 0) assert.equal(d.getAllData().length, 0)
}) })
it('Constructor compatibility with v0.6-', () => {
const db1 = new Datastore('somefile')
assert.equal(db1.filename, 'somefile')
assert.equal(db1.inMemoryOnly, false)
const db2 = new Datastore('')
assert.equal(db2.filename, null)
assert.equal(db2.inMemoryOnly, true)
const db3 = new Datastore()
assert.equal(db3.filename, null)
assert.equal(db3.inMemoryOnly, true)
})
describe('Autoloading', () => { describe('Autoloading', () => {
it('Can autoload a database and query it right away', async () => { it('Can autoload a database and query it right away', async () => {
const fileStr = model.serialize({ const fileStr = model.serialize({

@ -40,20 +40,6 @@ describe('Database', function () {
], done) ], done)
}) })
it('Constructor compatibility with v0.6-', function () {
let dbef = new Datastore('somefile')
dbef.filename.should.equal('somefile')
dbef.inMemoryOnly.should.equal(false)
dbef = new Datastore('')
assert.isNull(dbef.filename)
dbef.inMemoryOnly.should.equal(true)
dbef = new Datastore()
assert.isNull(dbef.filename)
dbef.inMemoryOnly.should.equal(true)
})
describe('Autoloading', function () { describe('Autoloading', function () {
it('Can autoload a database and query it right away', function (done) { it('Can autoload a database and query it right away', function (done) {
const fileStr = model.serialize({ _id: '1', a: 5, planet: 'Earth' }) + '\n' + model.serialize({ const fileStr = model.serialize({ _id: '1', a: 5, planet: 'Earth' }) + '\n' + model.serialize({
@ -617,7 +603,7 @@ describe('Database', function () {
assert.isNull(err) assert.isNull(err)
assert.isNull(doc) assert.isNull(doc)
d.on('compaction.done', function () { d.oncompaction = function () {
// After compaction, no more mention of the document, correctly removed // After compaction, no more mention of the document, correctly removed
const datafileContents = fs.readFileSync(testDb, 'utf8') const datafileContents = fs.readFileSync(testDb, 'utf8')
datafileContents.split('\n').length.should.equal(2) datafileContents.split('\n').length.should.equal(2)
@ -631,7 +617,7 @@ describe('Database', function () {
done() done()
}) })
}) }
d.compactDatafile() d.compactDatafile()
}) })

@ -367,12 +367,16 @@ describe('Persistence async', function () {
}) })
}) })
it('Can listen to compaction events', async () => { it('Can use compaction callback', async () => {
const compacted = new Promise(resolve => { let resolve, reject
d.once('compaction.done', function () { const compacted = new Promise((_resolve, _reject) => {
resolve() resolve = _resolve
}) reject = _reject
}) })
d.oncompaction = err => {
if (err) reject(err)
else resolve(null)
}
await d.compactDatafileAsync() await d.compactDatafileAsync()
await compacted // should already be resolved when the function returns, but still awaiting for it await compacted // should already be resolved when the function returns, but still awaiting for it
}) })

@ -453,10 +453,10 @@ describe('Persistence', function () {
}) })
it('Can listen to compaction events', function (done) { it('Can listen to compaction events', function (done) {
d.on('compaction.done', function () { d.oncompaction = function () {
d.removeAllListeners('compaction.done') // Tidy up for next tests d.oncompaction = null // Tidy up for next tests
done() done()
}) }
d.compactDatafile() d.compactDatafile()
}) })

@ -4,42 +4,16 @@ const path = require('path')
const webpack = require('webpack') const webpack = require('webpack')
module.exports = (env, argv) => { module.exports = (env, argv) => {
const minimize = argv.optimizationMinimize || false const minimize = argv.env.minimize || false
return {
const baseConfig = {
mode: 'production', mode: 'production',
cache: false, cache: false,
watch: false, watch: false,
target: 'web', target: 'web',
node: {
global: true
},
optimization: { optimization: {
minimize minimize
}, },
resolve: {
fallback: {
fs: false,
path: require.resolve('path-browserify'),
events: require.resolve('events/'),
crypto: false
}
},
plugins: [
new webpack.NormalModuleReplacementPlugin(new RegExp(path.resolve(__dirname, 'lib/storage.js')), path.resolve(__dirname, 'browser-version/lib/storage.browser.js')),
new webpack.NormalModuleReplacementPlugin(new RegExp(path.resolve(__dirname, 'lib/customUtils.js')), path.resolve(__dirname, 'browser-version/lib/customUtils.js')),
new webpack.NormalModuleReplacementPlugin(/byline/, path.resolve(__dirname, 'browser-version/lib/byline.js')),
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
setImmediate: ['timers-browserify', 'setImmediate'],
clearImmediate: ['timers-browserify', 'clearImmediate'],
util: 'util'
})
],
entry: {
Nedb: path.join(__dirname, 'lib', 'datastore.js'),
testUtils: path.join(__dirname, 'test', 'utils.test.js')
},
output: { output: {
path: path.join(__dirname, 'browser-version/out'), path: path.join(__dirname, 'browser-version/out'),
filename: pathData => `${pathData.chunk.name.toLowerCase()}${minimize ? '.min' : ''}.js`, filename: pathData => `${pathData.chunk.name.toLowerCase()}${minimize ? '.min' : ''}.js`,
@ -47,4 +21,46 @@ module.exports = (env, argv) => {
library: '[name]' library: '[name]'
} }
} }
const pluginsNedb = [
new webpack.NormalModuleReplacementPlugin(new RegExp(path.resolve(__dirname, 'lib/storage.js')), path.resolve(__dirname, 'browser-version/lib/storage.browser.js')),
new webpack.NormalModuleReplacementPlugin(new RegExp(path.resolve(__dirname, 'lib/customUtils.js')), path.resolve(__dirname, 'browser-version/lib/customUtils.js')),
new webpack.NormalModuleReplacementPlugin(/byline/, path.resolve(__dirname, 'browser-version/lib/byline.js'))
]
const polyfillPlugins = [
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
setImmediate: ['timers-browserify', 'setImmediate'],
clearImmediate: ['timers-browserify', 'clearImmediate']
})
]
return [
{
...baseConfig,
name: 'Nedb',
plugins: pluginsNedb,
entry: {
Nedb: path.join(__dirname, 'lib', 'datastore.js')
}
},
{
...baseConfig,
name: 'testUtils',
plugins: polyfillPlugins,
resolve: {
fallback: {
fs: false,
path: require.resolve('path-browserify'),
util: require.resolve('util/'),
crypto: false
}
},
entry: {
testUtils: path.join(__dirname, 'test', 'utils.test.js')
}
}
]
} }

Loading…
Cancel
Save