cleanup JSDoc

pull/11/head
Timothée Rebours 3 years ago
parent 3c60111c48
commit 8d628bfa6f
  1. 41
      browser-version/lib/storage.browser.js
  2. 39
      browser-version/lib/storage.react-native.js
  3. 17
      lib/executor.js
  4. 88
      lib/persistence.js
  5. 84
      lib/storage.js
  6. 12
      lib/utils.js
  7. 2
      lib/waterfall.js
  8. 2
      package.json

@ -1,10 +1,10 @@
/** /**
* Way data is stored for this database * Way data is stored for this database
* For a Node.js/Node Webkit database it's the file system
* For a browser-side database it's localforage which chooses the best option depending on user browser (IndexedDB then WebSQL then localStorage)
* *
* This version is the browser version * This version is the browser version and uses [localforage]{@link https://github.com/localForage/localForage} which chooses the best option depending on user browser (IndexedDB then WebSQL then localStorage).
* @module storageBrowser * @module storageBrowser
* @see module:storage
* @see module:storageReactNative
*/ */
const localforage = require('localforage') const localforage = require('localforage')
@ -17,11 +17,14 @@ const store = localforage.createInstance({
}) })
/** /**
* Returns Promise<true> if file exists * Returns Promise<true> if file exists.
*
* Async version of {@link module:storageBrowser.exists}.
* @param {string} file * @param {string} file
* @return {Promise<boolean>} * @return {Promise<boolean>}
* @async * @async
* @alias module:storageBrowser.existsAsync * @alias module:storageBrowser.existsAsync
* @see module:storageBrowser.exists
*/ */
const existsAsync = async file => { const existsAsync = async file => {
try { try {
@ -39,7 +42,7 @@ const existsAsync = async file => {
*/ */
/** /**
* Callback returns true if file exists * Callback returns true if file exists.
* @function * @function
* @param {string} file * @param {string} file
* @param {module:storageBrowser~existsCallback} cb * @param {module:storageBrowser~existsCallback} cb
@ -48,12 +51,13 @@ const existsAsync = async file => {
const exists = callbackify(existsAsync) const exists = callbackify(existsAsync)
/** /**
* Moves the item from one path to another * Async version of {@link module:storageBrowser.rename}.
* @param {string} oldPath * @param {string} oldPath
* @param {string} newPath * @param {string} newPath
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageBrowser.renameAsync * @alias module:storageBrowser.renameAsync
* @async * @async
* @see module:storageBrowser.rename
*/ */
const renameAsync = async (oldPath, newPath) => { const renameAsync = async (oldPath, newPath) => {
try { try {
@ -80,13 +84,14 @@ const renameAsync = async (oldPath, newPath) => {
const rename = callbackify(renameAsync) const rename = callbackify(renameAsync)
/** /**
* Saves the item at given path * Async version of {@link module:storageBrowser.writeFile}.
* @param {string} file * @param {string} file
* @param {string} data * @param {string} data
* @param {object} [options] * @param {object} [options]
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageBrowser.writeFileAsync * @alias module:storageBrowser.writeFileAsync
* @async * @async
* @see module:storageBrowser.writeFile
*/ */
const writeFileAsync = async (file, data, options) => { const writeFileAsync = async (file, data, options) => {
// Options do not matter in browser setup // Options do not matter in browser setup
@ -109,7 +114,7 @@ const writeFileAsync = async (file, data, options) => {
const writeFile = callbackify(writeFileAsync) const writeFile = callbackify(writeFileAsync)
/** /**
* Append to the item at given path * Async version of {@link module:storageBrowser.appendFile}.
* @function * @function
* @param {string} filename * @param {string} filename
* @param {string} toAppend * @param {string} toAppend
@ -117,6 +122,7 @@ const writeFile = callbackify(writeFileAsync)
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageBrowser.appendFileAsync * @alias module:storageBrowser.appendFileAsync
* @async * @async
* @see module:storageBrowser.appendFile
*/ */
const appendFileAsync = async (filename, toAppend, options) => { const appendFileAsync = async (filename, toAppend, options) => {
// Options do not matter in browser setup // Options do not matter in browser setup
@ -140,13 +146,14 @@ const appendFileAsync = async (filename, toAppend, options) => {
const appendFile = callbackify(appendFileAsync) const appendFile = callbackify(appendFileAsync)
/** /**
* Read data at given path * Async version of {@link module:storageBrowser.readFile}.
* @function * @function
* @param {string} filename * @param {string} filename
* @param {object} [options] * @param {object} [options]
* @return {Promise<Buffer>} * @return {Promise<Buffer>}
* @alias module:storageBrowser.readFileAsync * @alias module:storageBrowser.readFileAsync
* @async * @async
* @see module:storageBrowser.readFile
*/ */
const readFileAsync = async (filename, options) => { const readFileAsync = async (filename, options) => {
try { try {
@ -167,12 +174,13 @@ const readFileAsync = async (filename, options) => {
const readFile = callbackify(readFileAsync) const readFile = callbackify(readFileAsync)
/** /**
* Remove the data at given path * Async version of {@link module:storageBrowser.unlink}.
* @function * @function
* @param {string} filename * @param {string} filename
* @return {Promise<void>} * @return {Promise<void>}
* @async * @async
* @alias module:storageBrowser.unlinkAsync * @alias module:storageBrowser.unlinkAsync
* @see module:storageBrowser.unlink
*/ */
const unlinkAsync = async filename => { const unlinkAsync = async filename => {
try { try {
@ -192,7 +200,7 @@ const unlinkAsync = async filename => {
const unlink = callbackify(unlinkAsync) const unlink = callbackify(unlinkAsync)
/** /**
* Shim for storage.mkdirAsync, nothing to do, no directories will be used on the browser * Shim for {@link module:storage.mkdirAsync}, nothing to do, no directories will be used on the browser.
* @function * @function
* @param {string} path * @param {string} path
* @param {object} [options] * @param {object} [options]
@ -202,7 +210,7 @@ const unlink = callbackify(unlinkAsync)
*/ */
const mkdirAsync = (path, options) => Promise.resolve() const mkdirAsync = (path, options) => Promise.resolve()
/** /**
* Shim for storage.mkdir, nothing to do, no directories will be used on the browser * Shim for {@link module:storage.mkdir}, nothing to do, no directories will be used on the browser.
* @function * @function
* @param {string} path * @param {string} path
* @param {object} options * @param {object} options
@ -212,8 +220,7 @@ const mkdirAsync = (path, options) => Promise.resolve()
const mkdir = callbackify(mkdirAsync) const mkdir = callbackify(mkdirAsync)
/** /**
* Ensure the datafile contains all the data, even if there was a crash during a full file write * Shim for {@link module:storage.ensureDatafileIntegrityAsync}, nothing to do, no data corruption possible in the browser.
* Nothing to do, no data corruption possible in the browser
* @param {string} filename * @param {string} filename
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageBrowser.ensureDatafileIntegrityAsync * @alias module:storageBrowser.ensureDatafileIntegrityAsync
@ -221,8 +228,7 @@ const mkdir = callbackify(mkdirAsync)
const ensureDatafileIntegrityAsync = (filename) => Promise.resolve() const ensureDatafileIntegrityAsync = (filename) => Promise.resolve()
/** /**
* Ensure the datafile contains all the data, even if there was a crash during a full file write * Shim for {@link module:storage.ensureDatafileIntegrity}, nothing to do, no data corruption possible in the browser.
* Nothing to do, no data corruption possible in the browser
* @function * @function
* @param {string} filename * @param {string} filename
* @param {NoParamCallback} callback signature: err * @param {NoParamCallback} callback signature: err
@ -231,11 +237,12 @@ const ensureDatafileIntegrityAsync = (filename) => Promise.resolve()
const ensureDatafileIntegrity = callbackify(ensureDatafileIntegrityAsync) const ensureDatafileIntegrity = callbackify(ensureDatafileIntegrityAsync)
/** /**
* Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost) * Async version of {@link module:storageBrowser.crashSafeWriteFileLines}.
* @param {string} filename * @param {string} filename
* @param {string[]} lines * @param {string[]} lines
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageBrowser.crashSafeWriteFileLinesAsync * @alias module:storageBrowser.crashSafeWriteFileLinesAsync
* @see module:storageBrowser.crashSafeWriteFileLines
*/ */
const crashSafeWriteFileLinesAsync = async (filename, lines) => { const crashSafeWriteFileLinesAsync = async (filename, lines) => {
lines.push('') // Add final new line lines.push('') // Add final new line

@ -1,21 +1,22 @@
/** /**
* Way data is stored for this database * Way data is stored for this database
* For a Node.js/Node Webkit database it's the file system
* For a browser-side database it's localforage, which uses the best backend available (IndexedDB then WebSQL then localStorage)
* For a react-native database, we use @react-native-async-storage/async-storage
* *
* This version is the react-native version * This version is the React-Native version and uses [@react-native-async-storage/async-storage]{@link https://github.com/react-native-async-storage/async-storage}.
* @module storageReactNative * @module storageReactNative
* @see module:storageBrowser
* @see module:storageReactNative
*/ */
const AsyncStorage = require('@react-native-async-storage/async-storage').default const AsyncStorage = require('@react-native-async-storage/async-storage').default
const { callbackify } = require('util') // TODO: util is not a dependency, this would fail if util is not polyfilled const { callbackify } = require('util') // TODO: util is not a dependency, this would fail if util is not polyfilled
/** /**
* Returns Promise<true> if file exists * Async version of {@link module:storageReactNative.exists}.
* @param {string} file * @param {string} file
* @return {Promise<boolean>} * @return {Promise<boolean>}
* @async * @async
* @alias module:storageReactNative.existsAsync * @alias module:storageReactNative.existsAsync
* @see module:storageReactNative.exists
*/ */
const existsAsync = async file => { const existsAsync = async file => {
try { try {
@ -41,12 +42,13 @@ const existsAsync = async file => {
const exists = callbackify(existsAsync) const exists = callbackify(existsAsync)
/** /**
* Moves the item from one path to another * Async version of {@link module:storageReactNative.rename}.
* @param {string} oldPath * @param {string} oldPath
* @param {string} newPath * @param {string} newPath
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageReactNative.renameAsync * @alias module:storageReactNative.renameAsync
* @async * @async
* @see module:storageReactNative.rename
*/ */
const renameAsync = async (oldPath, newPath) => { const renameAsync = async (oldPath, newPath) => {
try { try {
@ -73,13 +75,14 @@ const renameAsync = async (oldPath, newPath) => {
const rename = callbackify(renameAsync) const rename = callbackify(renameAsync)
/** /**
* Saves the item at given path * Async version of {@link module:storageReactNative.writeFile}.
* @param {string} file * @param {string} file
* @param {string} data * @param {string} data
* @param {object} [options] * @param {object} [options]
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageReactNative.writeFileAsync * @alias module:storageReactNative.writeFileAsync
* @async * @async
* @see module:storageReactNative.writeFile
*/ */
const writeFileAsync = async (file, data, options) => { const writeFileAsync = async (file, data, options) => {
// Options do not matter in browser setup // Options do not matter in browser setup
@ -102,7 +105,7 @@ const writeFileAsync = async (file, data, options) => {
const writeFile = callbackify(writeFileAsync) const writeFile = callbackify(writeFileAsync)
/** /**
* Append to the item at given path * Async version of {@link module:storageReactNative.appendFile}.
* @function * @function
* @param {string} filename * @param {string} filename
* @param {string} toAppend * @param {string} toAppend
@ -110,6 +113,7 @@ const writeFile = callbackify(writeFileAsync)
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageReactNative.appendFileAsync * @alias module:storageReactNative.appendFileAsync
* @async * @async
* @see module:storageReactNative.appendFile
*/ */
const appendFileAsync = async (filename, toAppend, options) => { const appendFileAsync = async (filename, toAppend, options) => {
// Options do not matter in browser setup // Options do not matter in browser setup
@ -133,13 +137,14 @@ const appendFileAsync = async (filename, toAppend, options) => {
const appendFile = callbackify(appendFileAsync) const appendFile = callbackify(appendFileAsync)
/** /**
* Read data at given path * Async version of {@link module:storageReactNative.readFile}.
* @function * @function
* @param {string} filename * @param {string} filename
* @param {object} [options] * @param {object} [options]
* @return {Promise<string>} * @return {Promise<string>}
* @alias module:storageReactNative.readFileAsync * @alias module:storageReactNative.readFileAsync
* @async * @async
* @see module:storageReactNative.readFile
*/ */
const readFileAsync = async (filename, options) => { const readFileAsync = async (filename, options) => {
try { try {
@ -161,12 +166,13 @@ const readFileAsync = async (filename, options) => {
const readFile = callbackify(readFileAsync) const readFile = callbackify(readFileAsync)
/** /**
* Remove the data at given path * Async version of {@link module:storageReactNative.unlink}.
* @function * @function
* @param {string} filename * @param {string} filename
* @return {Promise<void>} * @return {Promise<void>}
* @async * @async
* @alias module:storageReactNative.unlinkAsync * @alias module:storageReactNative.unlinkAsync
* @see module:storageReactNative.unlink
*/ */
const unlinkAsync = async filename => { const unlinkAsync = async filename => {
try { try {
@ -186,7 +192,7 @@ const unlinkAsync = async filename => {
const unlink = callbackify(unlinkAsync) const unlink = callbackify(unlinkAsync)
/** /**
* Shim for storage.mkdirAsync, nothing to do, no directories will be used on the browser * Shim for {@link module:storage.mkdirAsync}, nothing to do, no directories will be used on the browser.
* @function * @function
* @param {string} dir * @param {string} dir
* @param {object} [options] * @param {object} [options]
@ -197,7 +203,7 @@ const unlink = callbackify(unlinkAsync)
const mkdirAsync = (dir, options) => Promise.resolve() const mkdirAsync = (dir, options) => Promise.resolve()
/** /**
* Shim for storage.mkdir, nothing to do, no directories will be used on the browser * Shim for {@link module:storage.mkdir}, nothing to do, no directories will be used on the browser.
* @function * @function
* @param {string} path * @param {string} path
* @param {object} options * @param {object} options
@ -207,8 +213,7 @@ const mkdirAsync = (dir, options) => Promise.resolve()
const mkdir = callbackify(mkdirAsync) const mkdir = callbackify(mkdirAsync)
/** /**
* Ensure the datafile contains all the data, even if there was a crash during a full file write * Shim for {@link module:storage.ensureDatafileIntegrityAsync}, nothing to do, no data corruption possible in the browser.
* Nothing to do, no data corruption possible in the browser
* @param {string} filename * @param {string} filename
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageReactNative.ensureDatafileIntegrityAsync * @alias module:storageReactNative.ensureDatafileIntegrityAsync
@ -216,8 +221,7 @@ const mkdir = callbackify(mkdirAsync)
const ensureDatafileIntegrityAsync = (filename) => Promise.resolve() const ensureDatafileIntegrityAsync = (filename) => Promise.resolve()
/** /**
* Ensure the datafile contains all the data, even if there was a crash during a full file write * Shim for {@link module:storage.ensureDatafileIntegrity}, nothing to do, no data corruption possible in the browser.
* Nothing to do, no data corruption possible in the browser
* @function * @function
* @param {string} filename * @param {string} filename
* @param {NoParamCallback} callback signature: err * @param {NoParamCallback} callback signature: err
@ -226,11 +230,12 @@ const ensureDatafileIntegrityAsync = (filename) => Promise.resolve()
const ensureDatafileIntegrity = callbackify(ensureDatafileIntegrityAsync) const ensureDatafileIntegrity = callbackify(ensureDatafileIntegrityAsync)
/** /**
* Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost) * Async version of {@link module:storageReactNative.crashSafeWriteFileLines}.
* @param {string} filename * @param {string} filename
* @param {string[]} lines * @param {string[]} lines
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storageReactNative.crashSafeWriteFileLinesAsync * @alias module:storageReactNative.crashSafeWriteFileLinesAsync
* @see module:storageReactNative.crashSafeWriteFileLines
*/ */
const crashSafeWriteFileLinesAsync = async (filename, lines) => { const crashSafeWriteFileLinesAsync = async (filename, lines) => {
lines.push('') // Add final new line lines.push('') // Add final new line

@ -12,19 +12,19 @@ class Executor {
/** /**
* If this.ready is `false`, then every task pushed will be buffered until this.processBuffer is called. * If this.ready is `false`, then every task pushed will be buffered until this.processBuffer is called.
* @type {boolean} * @type {boolean}
* @protected * @private
*/ */
this.ready = false this.ready = false
/** /**
* The main queue * The main queue
* @type {Waterfall} * @type {Waterfall}
* @protected * @private
*/ */
this.queue = new Waterfall() this.queue = new Waterfall()
/** /**
* The buffer queue * The buffer queue
* @type {Waterfall} * @type {Waterfall}
* @protected * @private
*/ */
this.buffer = new Waterfall() this.buffer = new Waterfall()
this.buffer.chain(new Promise(resolve => { this.buffer.chain(new Promise(resolve => {
@ -33,7 +33,7 @@ class Executor {
* *
* Do not be use directly, use `this.processBuffer` instead. * Do not be use directly, use `this.processBuffer` instead.
* @function * @function
* @protected * @private
*/ */
this._triggerBuffer = resolve this._triggerBuffer = resolve
})) }))
@ -78,12 +78,15 @@ class Executor {
} }
/** /**
* If executor is ready, queue task (and process it immediately if executor was idle) * Async version of {@link Executor#push}.
* If not, buffer task for later processing * This version is way simpler than its callbackEquivalent: you give it an async function `task`, it is executed when
* @param {function(...*):Promise<*>} task * all the previous tasks are done, and then resolves or rejects and when it is finished with its original result or
* error.
* @param {AsyncFunction} task
* @param {boolean} [forceQueuing = false] * @param {boolean} [forceQueuing = false]
* @return {Promise<*>} * @return {Promise<*>}
* @async * @async
* @see Executor#push
*/ */
pushAsync (task, forceQueuing = false) { pushAsync (task, forceQueuing = false) {
if (this.ready || forceQueuing) return this.queue.waterfall(task)() if (this.ready || forceQueuing) return this.queue.waterfall(task)()

@ -15,8 +15,8 @@ class Persistence {
* @param {Datastore} options.db * @param {Datastore} options.db
* @param {Number} [options.corruptAlertThreshold] Optional, threshold after which an alert is thrown if too much data is corrupt * @param {Number} [options.corruptAlertThreshold] Optional, threshold after which an alert is thrown if too much data is corrupt
* @param {string} [options.nodeWebkitAppName] Optional, specify the name of your NW app if you want options.filename to be relative to the directory where Node Webkit stores application data such as cookies and local storage (the best place to store data in my opinion) * @param {string} [options.nodeWebkitAppName] Optional, specify the name of your NW app if you want options.filename to be relative to the directory where Node Webkit stores application data such as cookies and local storage (the best place to store data in my opinion)
* @param {function} [options.beforeDeserialization] Hook you can use to transform data after it was serialized and before it is written to disk. * @param {serializationHook} [options.beforeDeserialization] Hook you can use to transform data after it was serialized and before it is written to disk.
* @param {function} [options.afterSerialization] Inverse of `afterSerialization`. * @param {serializationHook} [options.afterSerialization] Inverse of `afterSerialization`.
*/ */
constructor (options) { constructor (options) {
this.db = options.db this.db = options.db
@ -64,19 +64,22 @@ class Persistence {
* Persist cached database * Persist cached database
* This serves as a compaction function since the cache always contains only the number of documents in the collection * This serves as a compaction function since the cache always contains only the number of documents in the collection
* while the data file is append-only so it may grow larger * while the data file is append-only so it may grow larger
* This is an internal function, use compactDataFile which uses the executor *
* @param {NoParamCallback} callback Optional callback, signature: err * This is an internal function, use {@link Persistence#compactDatafile} which uses the [executor]{@link Datastore#executor}.
* @param {NoParamCallback} [callback = () => {}]
* @protected
*/ */
persistCachedDatabase (callback = () => {}) { persistCachedDatabase (callback = () => {}) {
return callbackify(this.persistCachedDatabaseAsync.bind(this))(callback) return callbackify(this.persistCachedDatabaseAsync.bind(this))(callback)
} }
/** /**
* Persist cached database * Async version of {@link Persistence#persistCachedDatabase}.
* This serves as a compaction function since the cache always contains only the number of documents in the collection *
* while the data file is append-only so it may grow larger * This is an internal function, use {@link Persistence#compactDatafileAsync} which uses the [executor]{@link Datastore#executor}.
* This is an internal function, use compactDataFileAsync which uses the executor
* @return {Promise<void>} * @return {Promise<void>}
* @protected
* @see Persistence#persistCachedDatabase
*/ */
async persistCachedDatabaseAsync () { async persistCachedDatabaseAsync () {
const lines = [] const lines = []
@ -104,23 +107,25 @@ class Persistence {
/** /**
* Queue a rewrite of the datafile * Queue a rewrite of the datafile
* @param {NoParamCallback} [callback = () => {}] Optional callback, signature: err * @param {NoParamCallback} [callback = () => {}]
* @see Persistence#persistCachedDatabase
*/ */
compactDatafile (callback = () => {}) { compactDatafile (callback = () => {}) {
this.db.executor.push({ this: this, fn: this.persistCachedDatabase, arguments: [callback] }) this.db.executor.push({ this: this, fn: this.persistCachedDatabase, arguments: [callback] })
} }
/** /**
* Queue a rewrite of the datafile * Async version of {@link Persistence#compactDatafile}.
* @async * @async
* @see Persistence#compactDatafile
*/ */
compactDatafileAsync () { compactDatafileAsync () {
return this.db.executor.pushAsync(() => this.persistCachedDatabaseAsync()) return this.db.executor.pushAsync(() => this.persistCachedDatabaseAsync())
} }
/** /**
* Set automatic compaction every interval ms * Set automatic compaction every `interval` ms
* @param {Number} interval in milliseconds, with an enforced minimum of 5 seconds * @param {Number} interval in milliseconds, with an enforced minimum of 5000 milliseconds
*/ */
setAutocompactionInterval (interval) { setAutocompactionInterval (interval) {
const minInterval = 5000 const minInterval = 5000
@ -134,7 +139,7 @@ class Persistence {
} }
/** /**
* Stop autocompaction (do nothing if autocompaction was not running) * Stop autocompaction (do nothing if automatic compaction was not running)
*/ */
stopAutocompaction () { stopAutocompaction () {
if (this.autocompactionIntervalId) clearInterval(this.autocompactionIntervalId) if (this.autocompactionIntervalId) clearInterval(this.autocompactionIntervalId)
@ -143,18 +148,24 @@ class Persistence {
/** /**
* Persist new state for the given newDocs (can be insertion, update or removal) * Persist new state for the given newDocs (can be insertion, update or removal)
* Use an append-only format * Use an append-only format
*
* Do not use directly, it should only used by a {@link Datastore} instance.
* @param {string[]} newDocs Can be empty if no doc was updated/removed * @param {string[]} newDocs Can be empty if no doc was updated/removed
* @param {NoParamCallback} [callback = () => {}] Optional, signature: err * @param {NoParamCallback} [callback = () => {}]
* @protected
*/ */
persistNewState (newDocs, callback = () => {}) { persistNewState (newDocs, callback = () => {}) {
callbackify(this.persistNewStateAsync.bind(this))(newDocs, err => callback(err)) callbackify(this.persistNewStateAsync.bind(this))(newDocs, err => callback(err))
} }
/** /**
* Persist new state for the given newDocs (can be insertion, update or removal) * Async version of {@link Persistence#persistNewState}
* Use an append-only format *
* Do not use directly, it should only used by a {@link Datastore} instance.
* @param {document[]} newDocs Can be empty if no doc was updated/removed * @param {document[]} newDocs Can be empty if no doc was updated/removed
* @return {Promise} * @return {Promise}
* @protected
* @see Persistence#persistNewState
*/ */
async persistNewStateAsync (newDocs) { async persistNewStateAsync (newDocs) {
let toPersist = '' let toPersist = ''
@ -179,9 +190,12 @@ class Persistence {
*/ */
/** /**
* From a database's raw data, return the corresponding machine understandable collection * From a database's raw data, return the corresponding machine understandable collection.
*
* Do not use directly, it should only used by a {@link Datastore} instance.
* @param {string} rawData database file * @param {string} rawData database file
* @return {{data: document[], indexes: Object.<string, rawIndex>}} * @return {{data: document[], indexes: Object.<string, rawIndex>}}
* @protected
*/ */
treatRawData (rawData) { treatRawData (rawData) {
const data = rawData.split('\n') const data = rawData.split('\n')
@ -218,13 +232,23 @@ class Persistence {
/** /**
* @callback Persistence~treatRawStreamCallback * @callback Persistence~treatRawStreamCallback
* @param {?Error} err * @param {?Error} err
* @param {{data: document[], indexes: Object.<string, rawIndex>}?} data * @param {?object} data
* @param {document[]} data.data
* @param {Object.<string, rawIndex>} data.indexes
*/ */
/** /**
* From a database's raw data stream, return the corresponding machine understandable collection * From a database's raw data stream, return the corresponding machine understandable collection
* Is only used by a {@link Datastore} instance.
*
* Is only used in the Node.js version, since [React-Native]{@link module:storageReactNative} &
* [browser]{@link module:storageBrowser} storage modules don't provide an equivalent of
* {@link module:storage.readFileStream}.
*
* Do not use directly, it should only used by a {@link Datastore} instance.
* @param {Readable} rawStream * @param {Readable} rawStream
* @param {Persistence~treatRawStreamCallback} cb * @param {Persistence~treatRawStreamCallback} cb
* @protected
*/ */
treatRawStream (rawStream, cb) { treatRawStream (rawStream, cb) {
const dataById = {} const dataById = {}
@ -270,10 +294,14 @@ class Persistence {
} }
/** /**
* From a database's raw data stream, return the corresponding machine understandable collection * Async version of {@link Persistence#treatRawStream}.
*
* Do not use directly, it should only used by a {@link Datastore} instance.
* @param {Readable} rawStream * @param {Readable} rawStream
* @return {Promise<{data: document[], indexes: Object.<string, rawIndex>}>} * @return {Promise<{data: document[], indexes: Object.<string, rawIndex>}>}
* @async * @async
* @protected
* @see Persistence#treatRawStream
*/ */
treatRawStreamAsync (rawStream) { treatRawStreamAsync (rawStream) {
return promisify(this.treatRawStream.bind(this))(rawStream) return promisify(this.treatRawStream.bind(this))(rawStream)
@ -284,24 +312,23 @@ class Persistence {
* 1) Create all indexes * 1) Create all indexes
* 2) Insert all data * 2) Insert all data
* 3) Compact the database * 3) Compact the database
*
* This means pulling data out of the data file or creating it if it doesn't exist * This means pulling data out of the data file or creating it if it doesn't exist
* Also, all data is persisted right away, which has the effect of compacting the database file * Also, all data is persisted right away, which has the effect of compacting the database file
* This operation is very quick at startup for a big collection (60ms for ~10k docs) * This operation is very quick at startup for a big collection (60ms for ~10k docs)
* @param {NoParamCallback} callback Optional callback, signature: err *
* Do not use directly as it does not use the [Executor]{@link Datastore.executor}, use {@link Datastore#loadDatabase} instead.
* @param {NoParamCallback} callback
* @protected
*/ */
loadDatabase (callback = () => {}) { loadDatabase (callback = () => {}) {
callbackify(this.loadDatabaseAsync.bind(this))(err => callback(err)) callbackify(this.loadDatabaseAsync.bind(this))(err => callback(err))
} }
/** /**
* Load the database * Async version of {@link Persistence#loadDatabase}
* 1) Create all indexes
* 2) Insert all data
* 3) Compact the database
* This means pulling data out of the data file or creating it if it doesn't exist
* Also, all data is persisted right away, which has the effect of compacting the database file
* This operation is very quick at startup for a big collection (60ms for ~10k docs)
* @return {Promise<void>} * @return {Promise<void>}
* @see Persistence#loadDatabase
*/ */
async loadDatabaseAsync () { async loadDatabaseAsync () {
this.db.resetIndexes() this.db.resetIndexes()
@ -339,18 +366,19 @@ class Persistence {
} }
/** /**
* Check if a directory stat and create it on the fly if it is not the case * Check if a directory stat and create it on the fly if it is not the case.
* @param {string} dir * @param {string} dir
* @param {NoParamCallback} [callback = () => {}] optional callback, signature: err * @param {NoParamCallback} [callback = () => {}]
*/ */
static ensureDirectoryExists (dir, callback = () => {}) { static ensureDirectoryExists (dir, callback = () => {}) {
storage.mkdir(dir, { recursive: true }, err => { callback(err) }) storage.mkdir(dir, { recursive: true }, err => { callback(err) })
} }
/** /**
* Check if a directory stat and create it on the fly if it is not the case * Async version of {@link Persistence.ensureDirectoryExists}.
* @param {string} dir * @param {string} dir
* @return {Promise<void>} * @return {Promise<void>}
* @see Persistence.ensureDirectoryExists
*/ */
static async ensureDirectoryExistsAsync (dir) { static async ensureDirectoryExistsAsync (dir) {
await storage.mkdirAsync(dir, { recursive: true }) await storage.mkdirAsync(dir, { recursive: true })

@ -1,11 +1,10 @@
/** /**
* Way data is stored for this database * Way data is stored for this database.
* For a Node.js/Node Webkit database it's the file system * This version is the Node.js/Node Webkit version.
* For a browser-side database it's localforage, which uses the best backend available (IndexedDB then WebSQL then localStorage) * It's essentially fs, mkdirp and crash safe write and read functions.
* For a react-native database, we use @react-native-async-storage/async-storage
* *
* This version is the Node.js/Node Webkit version * @see module:storageBrowser
* It's essentially fs, mkdirp and crash safe write and read functions * @see module:storageReactNative
* @module storage * @module storage
*/ */
const fs = require('fs') const fs = require('fs')
@ -20,7 +19,7 @@ const { Readable } = require('stream')
*/ */
/** /**
* Callback returns true if file exists * Callback returns true if file exists.
* @param {string} file * @param {string} file
* @param {module:storage~existsCallback} cb * @param {module:storage~existsCallback} cb
* @alias module:storage.exists * @alias module:storage.exists
@ -29,16 +28,17 @@ const { Readable } = require('stream')
const exists = (file, cb) => fs.access(file, fs.constants.F_OK, (err) => { cb(!err) }) const exists = (file, cb) => fs.access(file, fs.constants.F_OK, (err) => { cb(!err) })
/** /**
* Returns Promise<true> if file exists * Async version of {@link module:storage.exists}.
* @param {string} file * @param {string} file
* @return {Promise<boolean>} * @return {Promise<boolean>}
* @async * @async
* @alias module:storage.existsAsync * @alias module:storage.existsAsync
* @see module:storage.exists
*/ */
const existsAsync = file => fsPromises.access(file, fs.constants.F_OK).then(() => true, () => false) const existsAsync = file => fsPromises.access(file, fs.constants.F_OK).then(() => true, () => false)
/** /**
* Node.js' fs.rename * Node.js' [fs.rename]{@link https://nodejs.org/api/fs.html#fsrenameoldpath-newpath-callback}.
* @function * @function
* @param {string} oldPath * @param {string} oldPath
* @param {string} newPath * @param {string} newPath
@ -49,18 +49,19 @@ const existsAsync = file => fsPromises.access(file, fs.constants.F_OK).then(() =
const rename = fs.rename const rename = fs.rename
/** /**
* Node.js' fs.promises.rename * Async version of {@link module:storage.rename}.
* @function * @function
* @param {string} oldPath * @param {string} oldPath
* @param {string} newPath * @param {string} newPath
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.renameAsync * @alias module:storage.renameAsync
* @async * @async
* @see module:storage.rename
*/ */
const renameAsync = fsPromises.rename const renameAsync = fsPromises.rename
/** /**
* Node.js' fs.writeFile * Node.js' [fs.writeFile]{@link https://nodejs.org/api/fs.html#fswritefilefile-data-options-callback}.
* @function * @function
* @param {string} path * @param {string} path
* @param {string} data * @param {string} data
@ -71,7 +72,7 @@ const renameAsync = fsPromises.rename
const writeFile = fs.writeFile const writeFile = fs.writeFile
/** /**
* Node.js' fs.promises.writeFile * Async version of {@link module:storage.writeFile}.
* @function * @function
* @param {string} path * @param {string} path
* @param {string} data * @param {string} data
@ -79,11 +80,12 @@ const writeFile = fs.writeFile
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.writeFileAsync * @alias module:storage.writeFileAsync
* @async * @async
* @see module:storage.writeFile
*/ */
const writeFileAsync = fsPromises.writeFile const writeFileAsync = fsPromises.writeFile
/** /**
* Node.js' fs.createWriteStream * Node.js' [fs.createWriteStream]{@link https://nodejs.org/api/fs.html#fscreatewritestreampath-options}.
* @function * @function
* @param {string} path * @param {string} path
* @param {Object} [options] * @param {Object} [options]
@ -93,7 +95,7 @@ const writeFileAsync = fsPromises.writeFile
const writeFileStream = fs.createWriteStream const writeFileStream = fs.createWriteStream
/** /**
* Node.js' fs.unlink * Node.js' [fs.unlink]{@link https://nodejs.org/api/fs.html#fsunlinkpath-callback}.
* @function * @function
* @param {string} path * @param {string} path
* @param {function} callback * @param {function} callback
@ -102,17 +104,18 @@ const writeFileStream = fs.createWriteStream
const unlink = fs.unlink const unlink = fs.unlink
/** /**
* Node.js' fs.promises.unlink * Async version of {@link module:storage.unlink}.
* @function * @function
* @param {string} path * @param {string} path
* @return {Promise<void>} * @return {Promise<void>}
* @async * @async
* @alias module:storage.unlinkAsync * @alias module:storage.unlinkAsync
* @see module:storage.unlink
*/ */
const unlinkAsync = fsPromises.unlink const unlinkAsync = fsPromises.unlink
/** /**
* Node.js' fs.appendFile * Node.js' [fs.appendFile]{@link https://nodejs.org/api/fs.html#fsappendfilepath-data-options-callback}.
* @function * @function
* @param {string} path * @param {string} path
* @param {string} data * @param {string} data
@ -123,7 +126,7 @@ const unlinkAsync = fsPromises.unlink
const appendFile = fs.appendFile const appendFile = fs.appendFile
/** /**
* Node.js' fs.promises.appendFile * Async version of {@link module:storage.appendFile}.
* @function * @function
* @param {string} path * @param {string} path
* @param {string} data * @param {string} data
@ -131,10 +134,12 @@ const appendFile = fs.appendFile
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.appendFileAsync * @alias module:storage.appendFileAsync
* @async * @async
* @see module:storage.appendFile
*/ */
const appendFileAsync = fsPromises.appendFile const appendFileAsync = fsPromises.appendFile
/** /**
* Node.js' fs.readFile * Node.js' [fs.readFile]{@link https://nodejs.org/api/fs.html#fsreadfilepath-options-callback}
* @function * @function
* @param {string} path * @param {string} path
* @param {object} options * @param {object} options
@ -142,18 +147,21 @@ const appendFileAsync = fsPromises.appendFile
* @alias module:storage.readFile * @alias module:storage.readFile
*/ */
const readFile = fs.readFile const readFile = fs.readFile
/** /**
* Node.js' fs.promises.readFile * Async version of {@link module:storage.readFile}.
* @function * @function
* @param {string} path * @param {string} path
* @param {object} [options] * @param {object} [options]
* @return {Promise<Buffer>} * @return {Promise<Buffer>}
* @alias module:storage.readFileAsync * @alias module:storage.readFileAsync
* @async * @async
* @see module:storage.readFile
*/ */
const readFileAsync = fsPromises.readFile const readFileAsync = fsPromises.readFile
/** /**
* Node.js' fs.createReadStream * Node.js' [fs.createReadStream]{@link https://nodejs.org/api/fs.html#fscreatereadstreampath-options}.
* @function * @function
* @param {string} path * @param {string} path
* @param {Object} [options] * @param {Object} [options]
@ -161,8 +169,9 @@ const readFileAsync = fsPromises.readFile
* @alias module:storage.readFileStream * @alias module:storage.readFileStream
*/ */
const readFileStream = fs.createReadStream const readFileStream = fs.createReadStream
/** /**
* Node.js' fs.mkdir * Node.js' [fs.mkdir]{@link https://nodejs.org/api/fs.html#fsmkdirpath-options-callback}.
* @function * @function
* @param {string} path * @param {string} path
* @param {object} options * @param {object} options
@ -170,28 +179,33 @@ const readFileStream = fs.createReadStream
* @alias module:storage.mkdir * @alias module:storage.mkdir
*/ */
const mkdir = fs.mkdir const mkdir = fs.mkdir
/** /**
* Node.js' fs.promises.mkdir * Async version of {@link module:storage.mkdir}.
* @function * @function
* @param {string} path * @param {string} path
* @param {object} options * @param {object} options
* @return {Promise<void|string>} * @return {Promise<void|string>}
* @alias module:storage.mkdirAsync * @alias module:storage.mkdirAsync
* @async * @async
* @see module:storage.mkdir
*/ */
const mkdirAsync = fsPromises.mkdir const mkdirAsync = fsPromises.mkdir
/** /**
* Async version of {@link module:storage.ensureFileDoesntExist}
* @param {string} file * @param {string} file
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.ensureFileDoesntExistAsync * @alias module:storage.ensureFileDoesntExistAsync
* @async * @async
* @see module:storage.ensureFileDoesntExist
*/ */
const ensureFileDoesntExistAsync = async file => { const ensureFileDoesntExistAsync = async file => {
if (await existsAsync(file)) await unlinkAsync(file) if (await existsAsync(file)) await unlinkAsync(file)
} }
/** /**
* Removes file if it exists.
* @param {string} file * @param {string} file
* @param {NoParamCallback} callback * @param {NoParamCallback} callback
* @alias module:storage.ensureFileDoesntExist * @alias module:storage.ensureFileDoesntExist
@ -199,7 +213,7 @@ const ensureFileDoesntExistAsync = async file => {
const ensureFileDoesntExist = (file, callback) => callbackify(ensureFileDoesntExistAsync)(file, err => callback(err)) const ensureFileDoesntExist = (file, callback) => callbackify(ensureFileDoesntExistAsync)(file, err => callback(err))
/** /**
* Flush data in OS buffer to storage if corresponding option is set * Flush data in OS buffer to storage if corresponding option is set.
* @param {object|string} options If options is a string, it is assumed that the flush of the file (not dir) called options was requested * @param {object|string} options If options is a string, it is assumed that the flush of the file (not dir) called options was requested
* @param {string} [options.filename] * @param {string} [options.filename]
* @param {boolean} [options.isDir = false] Optional, defaults to false * @param {boolean} [options.isDir = false] Optional, defaults to false
@ -209,13 +223,14 @@ const ensureFileDoesntExist = (file, callback) => callbackify(ensureFileDoesntEx
const flushToStorage = (options, callback) => callbackify(flushToStorageAsync)(options, callback) const flushToStorage = (options, callback) => callbackify(flushToStorageAsync)(options, callback)
/** /**
* Flush data in OS buffer to storage if corresponding option is set * Async version of {@link module:storage.flushToStorage}.
* @param {object|string} options If options is a string, it is assumed that the flush of the file (not dir) called options was requested * @param {object|string} options
* @param {string} [options.filename] * @param {string} [options.filename]
* @param {boolean} [options.isDir = false] Optional, defaults to false * @param {boolean} [options.isDir = false]
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.flushToStorageAsync * @alias module:storage.flushToStorageAsync
* @async * @async
* @see module:storage.flushToStorage
*/ */
const flushToStorageAsync = async (options) => { const flushToStorageAsync = async (options) => {
let filename let filename
@ -266,10 +281,10 @@ const flushToStorageAsync = async (options) => {
} }
/** /**
* Fully write or rewrite the datafile * Fully write or rewrite the datafile.
* @param {string} filename * @param {string} filename
* @param {string[]} lines * @param {string[]} lines
* @param {NoParamCallback} callback * @param {NoParamCallback} [callback = () => {}]
* @alias module:storage.writeFileLines * @alias module:storage.writeFileLines
*/ */
const writeFileLines = (filename, lines, callback = () => {}) => { const writeFileLines = (filename, lines, callback = () => {}) => {
@ -293,17 +308,18 @@ const writeFileLines = (filename, lines, callback = () => {}) => {
} }
} }
/** /**
* Fully write or rewrite the datafile * Async version of {@link module:storage.writeFileLines}.
* @param {string} filename * @param {string} filename
* @param {string[]} lines * @param {string[]} lines
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.writeFileLinesAsync * @alias module:storage.writeFileLinesAsync
* @async * @async
* @see module:storage.writeFileLines
*/ */
const writeFileLinesAsync = (filename, lines) => promisify(writeFileLines)(filename, lines) const writeFileLinesAsync = (filename, lines) => promisify(writeFileLines)(filename, lines)
/** /**
* Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost) * Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost).
* @param {string} filename * @param {string} filename
* @param {string[]} lines * @param {string[]} lines
* @param {NoParamCallback} [callback] Optional callback, signature: err * @param {NoParamCallback} [callback] Optional callback, signature: err
@ -313,11 +329,12 @@ const crashSafeWriteFileLines = (filename, lines, callback = () => {}) => {
callbackify(crashSafeWriteFileLinesAsync)(filename, lines, callback) callbackify(crashSafeWriteFileLinesAsync)(filename, lines, callback)
} }
/** /**
* Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost) * Async version of {@link module:storage.crashSafeWriteFileLines}.
* @param {string} filename * @param {string} filename
* @param {string[]} lines * @param {string[]} lines
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.crashSafeWriteFileLinesAsync * @alias module:storage.crashSafeWriteFileLinesAsync
* @see module:storage.crashSafeWriteFileLines
*/ */
const crashSafeWriteFileLinesAsync = async (filename, lines) => { const crashSafeWriteFileLinesAsync = async (filename, lines) => {
const tempFilename = filename + '~' const tempFilename = filename + '~'
@ -337,7 +354,7 @@ const crashSafeWriteFileLinesAsync = async (filename, lines) => {
} }
/** /**
* Ensure the datafile contains all the data, even if there was a crash during a full file write * Ensure the datafile contains all the data, even if there was a crash during a full file write.
* @param {string} filename * @param {string} filename
* @param {NoParamCallback} callback signature: err * @param {NoParamCallback} callback signature: err
* @alias module:storage.ensureDatafileIntegrity * @alias module:storage.ensureDatafileIntegrity
@ -345,10 +362,11 @@ const crashSafeWriteFileLinesAsync = async (filename, lines) => {
const ensureDatafileIntegrity = (filename, callback) => callbackify(ensureDatafileIntegrityAsync)(filename, callback) const ensureDatafileIntegrity = (filename, callback) => callbackify(ensureDatafileIntegrityAsync)(filename, callback)
/** /**
* Ensure the datafile contains all the data, even if there was a crash during a full file write * Async version of {@link module:storage.ensureDatafileIntegrity}.
* @param {string} filename * @param {string} filename
* @return {Promise<void>} * @return {Promise<void>}
* @alias module:storage.ensureDatafileIntegrityAsync * @alias module:storage.ensureDatafileIntegrityAsync
* @see module:storage.ensureDatafileIntegrity
*/ */
const ensureDatafileIntegrityAsync = async filename => { const ensureDatafileIntegrityAsync = async filename => {
const tempFilename = filename + '~' const tempFilename = filename + '~'

@ -9,7 +9,8 @@
* Produces a duplicate-free version of the array, using === to test object equality. In particular only the first * Produces a duplicate-free version of the array, using === to test object equality. In particular only the first
* occurrence of each value is kept. If you want to compute unique items based on a transformation, pass an iteratee * occurrence of each value is kept. If you want to compute unique items based on a transformation, pass an iteratee
* function. * function.
* Heavily inspired by https://underscorejs.org/#uniq *
* Heavily inspired by {@link https://underscorejs.org/#uniq}.
* @param {Array} array * @param {Array} array
* @param {function} [iteratee] transformation applied to every element before checking for duplicates. This will not * @param {function} [iteratee] transformation applied to every element before checking for duplicates. This will not
* transform the items in the result. * transform the items in the result.
@ -23,7 +24,8 @@ const uniq = (array, iteratee) => {
/** /**
* Returns true if arg is an Object. Note that JavaScript arrays and functions are objects, while (normal) strings * Returns true if arg is an Object. Note that JavaScript arrays and functions are objects, while (normal) strings
* and numbers are not. * and numbers are not.
* Heavily inspired by https://underscorejs.org/#isObject *
* Heavily inspired by {@link https://underscorejs.org/#isObject}.
* @param {*} arg * @param {*} arg
* @return {boolean} * @return {boolean}
*/ */
@ -31,7 +33,8 @@ const isObject = arg => typeof arg === 'object' && arg !== null
/** /**
* Returns true if d is a Date. * Returns true if d is a Date.
* Heavily inspired by https://underscorejs.org/#isDate *
* Heavily inspired by {@link https://underscorejs.org/#isDate}.
* @param {*} d * @param {*} d
* @return {boolean} * @return {boolean}
* @alias module:utils.isDate * @alias module:utils.isDate
@ -40,7 +43,8 @@ const isDate = d => isObject(d) && Object.prototype.toString.call(d) === '[objec
/** /**
* Returns true if re is a RegExp. * Returns true if re is a RegExp.
* Heavily inspired by https://underscorejs.org/#isRegExp *
* Heavily inspired by {@link https://underscorejs.org/#isRegExp}.
* @param {*} re * @param {*} re
* @return {boolean} * @return {boolean}
* @alias module:utils.isRegExp * @alias module:utils.isRegExp

@ -13,7 +13,7 @@ class Waterfall {
* *
* Use {@link Waterfall#guardian} instead which retrievethe latest version of the guardian. * Use {@link Waterfall#guardian} instead which retrievethe latest version of the guardian.
* @type {Promise} * @type {Promise}
* @protected * @private
*/ */
this._guardian = Promise.resolve() this._guardian = Promise.resolve()
} }

@ -88,7 +88,7 @@
"test:typings": "ts-node ./typings-tests.ts", "test:typings": "ts-node ./typings-tests.ts",
"prepublishOnly": "npm run build:browser", "prepublishOnly": "npm run build:browser",
"generateDocs:markdown": "node jsdoc2md.js", "generateDocs:markdown": "node jsdoc2md.js",
"generateDocs:html": "jsdoc -c jsdoc.conf.js -d docs-html --readme README.md lib/*.js browser-version/lib/*.js" "generateDocs:html": "jsdoc -c jsdoc.conf.js -d docs-html --readme README.md"
}, },
"main": "index.js", "main": "index.js",
"browser": { "browser": {

Loading…
Cancel
Save