diff --git a/jsdoc.json b/jsdoc.json
new file mode 100644
index 0000000..533a731
--- /dev/null
+++ b/jsdoc.json
@@ -0,0 +1,3 @@
+{
+ "plugins": ["plugins/markdown"]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 65457d8..2ed9a25 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"dist"
],
"type": "module",
- "main": "src/OrbitDB.js",
+ "main": "src/orbitdb.js",
"dependencies": {
"@ipld/dag-cbor": "^9.0.0",
"@libp2p/crypto": "^1.0.12",
@@ -62,7 +62,7 @@
"build:examples": "webpack --config conf/webpack.example.config.js",
"build:dist": "webpack --config conf/webpack.config.js",
"build:debug": "webpack --config conf/webpack.debug.config.js",
- "build:docs": "jsdoc -r src/**",
+ "build:docs": "jsdoc -c ./jsdoc.json -r src/**",
"build:tests": "rm -f test/browser/bundle.js* && webpack --config ./conf/webpack.tests.config.js",
"prepublishOnly": "npm run build",
"lint": "standard --env=mocha",
diff --git a/src/access-controllers/index.js b/src/access-controllers/index.js
index 2670c43..d5650c4 100644
--- a/src/access-controllers/index.js
+++ b/src/access-controllers/index.js
@@ -1,12 +1,41 @@
-/** @module AccessControllers */
+/**
+ * @module AccessControllers
+ * @description
+ * Provides a platform for managing access controllers. Supported access
+ * controllers can be added and removed from the access controller list, and
+ * can load the associated module if they are supported.
+ *
+ * An AccessController module needs to only expose one function,
+ * canAppend(entry) which returns true if the entry can be appended to the
+ * oplog, or false otherwise:
+ * ```javascript
+ * const CustomAccessController = ({ write } = {}) => async => {
+ * const canAppend = async (entry) => {
+ * // Use entry.identity to determine whether the entry can be appended.
+ * // Return true if entry can be appended to OpLog.
+ * // Or return false otherwise.
+ * }
+ * }
+ */
import IPFSAccessController from './ipfs.js'
import OrbitDBAccessController from './orbitdb.js'
+/**
+ * An array of available access controller types.
+ * @name types
+ * @†ype []
+ * @return [] An array of access controller types.
+ */
const types = {
ipfs: IPFSAccessController,
orbitdb: OrbitDBAccessController
}
+/**
+ * Gets an access controller module specified by type.
+ * @param {string} type A valid access controller type.
+ * @return {AccessController} The access controller module.
+ */
const get = (type) => {
if (!isSupported(type)) {
throw new Error(`AccessController type '${type}' is not supported`)
@@ -14,10 +43,25 @@ const get = (type) => {
return types[type]
}
+/**
+ * Checks whether the access controller exists.
+ * @param {string} type A valid access controller type.
+ * @return {boolean} True if the access controller exists, false otherwise.
+ */
const isSupported = type => {
return Object.keys(types).includes(type)
}
+/**
+ * Adds an access controller module to the list of supported access controller
+ * types.
+ * @param {AccessController} accessController A compatible access controller
+ * module.
+ * @throws Access controller `type` already added if the access controller is
+ * already supported.
+ * @throws Given AccessController class needs to implement: type if the access
+ * controller module does not implement a type property.
+ */
const add = (accessController) => {
if (types[accessController.type]) {
throw new Error(`Access controller '${accessController.type}' already added.`)
@@ -30,6 +74,10 @@ const add = (accessController) => {
types[accessController.type] = accessController
}
+/**
+ * Removes an access controller from the types list.
+ * @param {string} type A valid access controller type.
+ */
const remove = type => {
delete types[type]
}
diff --git a/src/access-controllers/ipfs.js b/src/access-controllers/ipfs.js
index 0a13c2d..e530f20 100644
--- a/src/access-controllers/ipfs.js
+++ b/src/access-controllers/ipfs.js
@@ -26,6 +26,31 @@ const AccessControlList = async ({ storage, type, params }) => {
const type = 'ipfs'
+/**
+ * Creates an instance of IPFSAccessController.
+ * @callback IPFSAccessController
+ * @param {Object} params Various parameters for configuring the access
+ * controller.
+ * @param {module:OrbitDB} params.orbitdb An OrbitDB instance.
+ * @param {module:Identities} params.identities An Identities instance.
+ * @param {string} [params.address] The address of the database.
+ * @function
+ * @instance
+ * @async
+ * @memberof module:AccessControllers.AccessControllers-IPFS
+ */
+
+/**
+ * Defines an IPFS access controller.
+ * @param {Object} options Various options for configuring the
+ * IPFSAccessController.
+ * @param {Array} [params.write] An array of identity ids who can write to the
+ * database.
+ * @param {module:Storage} [params.storage] An instance of a compatible storage.
+ * @returns {module:AccessControllers.AccessControllers-IPFS} An
+ * IPFSAccessController function.
+ * @memberof module:AccessControllers
+ */
const IPFSAccessController = ({ write, storage } = {}) => async ({ orbitdb, identities, address }) => {
storage = storage || await ComposedStorage(
await LRUStorage({ size: 1000 }),
@@ -42,6 +67,13 @@ const IPFSAccessController = ({ write, storage } = {}) => async ({ orbitdb, iden
address = pathJoin('/', type, address)
}
+ /**
+ * Verifies the write permission of an entry.
+ * @param {module:Log~Entry} entry An entry to verify.
+ * @returns {boolean} True if the entry's identity has write permission,
+ * false otherwise.
+ * @memberof module:AccessControllers.AccessControllers-IPFS
+ */
const canAppend = async (entry) => {
const writerIdentity = await identities.getIdentity(entry.identity)
if (!writerIdentity) {
diff --git a/src/access-controllers/orbitdb.js b/src/access-controllers/orbitdb.js
index 08c04e5..bc2a2e9 100644
--- a/src/access-controllers/orbitdb.js
+++ b/src/access-controllers/orbitdb.js
@@ -7,6 +7,30 @@ import IPFSAccessController from './ipfs.js'
const type = 'orbitdb'
+/**
+ * Creates an instance of OrbitDBAccessController.
+ * @callback OrbitDBAccessController
+ * @param {Object} params Various parameters for configuring the access
+ * controller.
+ * @param {module:OrbitDB} params.orbitdb An OrbitDB instance.
+ * @param {module:Identities} params.identities An Identities instance.
+ * @param {string} [params.address] The address of the database.
+ * @function
+ * @instance
+ * @async
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ */
+
+/**
+ * Defines an OrbitDB access controller.
+ * @param {Object} options Various options for configuring the
+ * IPFSAccessController.
+ * @param {Array} [params.write] An array of identity ids who can write to the
+ * database.
+ * @returns {module:AccessControllers.AccessControllers-OrbitDB} An
+ * IPFSAccessController function.
+ * @memberof module:AccessControllers
+ */
const OrbitDBAccessController = ({ write } = {}) => async ({ orbitdb, identities, address }) => {
address = address || 'default-access-controller'
write = write || [orbitdb.identity.id]
@@ -15,7 +39,14 @@ const OrbitDBAccessController = ({ write } = {}) => async ({ orbitdb, identities
const db = await orbitdb.open(ensureACAddress(address), { type: 'keyvalue', AccessController: IPFSAccessController({ write }) })
address = db.address
- // Return true if entry is allowed to be added to the database
+ /**
+ * Verifies the write permission of an entry.
+ * @param {module:Log~Entry} entry An entry to verify.
+ * @returns {boolean} True if the entry's identity has write permission,
+ * false otherwise.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const canAppend = async (entry) => {
const writerIdentity = await identities.getIdentity(entry.identity)
if (!writerIdentity) {
@@ -32,6 +63,15 @@ const OrbitDBAccessController = ({ write } = {}) => async ({ orbitdb, identities
return false
}
+ /**
+ * Gets the access capabilities of the OrbitDB access controller.
+ *
+ * The returned capabilities will be a mixture of admin and write access
+ * addresses.
+ * @returns {Array} A list of addresses with admin and write access.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const capabilities = async () => {
const _capabilities = []
for await (const entry of db.iterator()) {
@@ -55,27 +95,64 @@ const OrbitDBAccessController = ({ write } = {}) => async ({ orbitdb, identities
return _capabilities
}
+ /**
+ * Gets a list of addresses with the specified capability.
+ * @param {string} capability A capability (e.g. write).
+ * @returns {Array} One or more addresses with the spcified capability.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const get = async (capability) => {
const _capabilities = await capabilities()
return _capabilities[capability] || new Set([])
}
+ /**
+ * Close the underlying access control database.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const close = async () => {
await db.close()
}
+ /**
+ * Checks whether an address has a capability.
+ * @param {string} capability A capability (e.g. write).
+ * @param {string} key An address.
+ * @returns {boolean} True if the address has the capability, false
+ * otherwise.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const hasCapability = async (capability, key) => {
// Write keys and admins keys are allowed
const access = new Set(await get(capability))
return access.has(key) || access.has('*')
}
+ /**
+ * Grants a capability to an address, storing it to the access control
+ * database.
+ * @param {string} capability A capability (e.g. write).
+ * @param {string} key An address.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const grant = async (capability, key) => {
// Merge current keys with the new key
const capabilities = new Set([...(await db.get(capability) || []), ...[key]])
await db.put(capability, Array.from(capabilities.values()))
}
+ /**
+ * Revokes a capability from an address, removing it from the access control
+ * database.
+ * @param {string} capability A capability (e.g. write).
+ * @param {string} key An address.
+ * @memberof module:AccessControllers.AccessControllers-OrbitDB
+ * @instance
+ */
const revoke = async (capability, key) => {
const capabilities = new Set(await db.get(capability) || [])
capabilities.delete(key)
diff --git a/src/address.js b/src/address.js
index b43f2e0..ebc8586 100644
--- a/src/address.js
+++ b/src/address.js
@@ -1,8 +1,19 @@
-/** @namespace Address */
+/**
+ * @module Address
+ * @description OrbitDB database address verification.
+ */
import { CID } from 'multiformats/cid'
import { base58btc } from 'multiformats/bases/base58'
import { posixJoin } from './utils/path-join.js'
+/**
+ * Validates an OrbitDB database address.
+ * @function
+ * @param {OrbitDBAddress|string} address An OrbitDB database address.
+ * @return {boolean} True if the address is a valid OrbitDB database address,
+ * false otherwise.
+ * @static
+ */
const isValidAddress = (address) => {
address = address.toString()
@@ -25,6 +36,15 @@ const isValidAddress = (address) => {
return cid !== undefined
}
+/**
+ * Parses an OrbitDB database address.
+ * @function
+ * @param {OrbitDBAddress|string} address A valid OrbitDB database address.
+ * @return {OrbitDBAddress} An instance of OrbitDBAddress.
+ * @throws Not a valid OrbitDB address if no address if provided.
+ * @throws Not a valid OrbitDB address if address is invalid.
+ * @static
+ */
const parseAddress = (address) => {
if (!address) {
throw new Error(`Not a valid OrbitDB address: ${address}`)
@@ -37,14 +57,41 @@ const parseAddress = (address) => {
return OrbitDBAddress(address)
}
+/**
+ * Creates an instance of OrbitDBAddress.
+ * @function
+ * @param {OrbitDBAddress|string} address A valid OrbitDB database address.
+ * @returns {OrbitDBAddress} An instance of OrbitDBAddress.
+ * @instance
+ */
const OrbitDBAddress = (address) => {
+ /**
+ * @namespace module:Address~OrbitDBAddress
+ * @description The instance returned by {@link module:Address~OrbitDBAddress}.
+ */
+
if (address && address.protocol === 'orbitdb' && address.path) {
return address
}
+ /**
+ * The 'orbitdb' protocol.
+ * @memberof module:Address~OrbitDBAddress
+ */
const protocol = 'orbitdb'
+
+ /**
+ * The path without the /orbitdb/ prefix.
+ * @memberof module:Address~OrbitDBAddress
+ */
const path = address.replace('/orbitdb/', '').replace('\\orbitdb\\', '')
+ /**
+ * Returns OrbitDBAddress as a string.
+ * @function
+ * @returns {string} The string form of OrbitDBAddress.
+ * @memberof module:Address~OrbitDBAddress
+ */
const toString = () => {
return posixJoin('/', protocol, path)
}
diff --git a/src/database.js b/src/database.js
index 01cb6f1..f13ff55 100644
--- a/src/database.js
+++ b/src/database.js
@@ -1,4 +1,38 @@
-/** @module Database */
+/**
+ * @module Database
+ * @description
+ * Database is the base class for OrbitDB data stores and handles all lower
+ * level add operations and database sync-ing using IPFS.
+ *
+ * Database should be instantiated and initialized when implementing a
+ * compatible datastore:
+ * ```
+ * const CustomDataStore = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
+ * const database = await Database({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate })
+ * const { addOperation, log } = database
+ *
+ * const put = async (key, value) => {
+ * return addOperation({ op: 'ADD', key, value })
+ * }
+ *
+ * const get = async (hash) => {
+ * const entry = await log.get(hash)
+ * return entry.payload.value
+ * }
+ *
+ * return {
+ * ...database,
+ * type: 'custom-data-store',
+ * put,
+ * get
+ * }
+ * }
+ *
+ * export default CustomDataStore
+ * ```
+ * The functions put and get are recommended but not mandatory. For example,
+ * the Events data store uses a function called `add`.
+ */
import { EventEmitter } from 'events'
import PQueue from 'p-queue'
import Sync from './sync.js'
@@ -9,10 +43,60 @@ import pathJoin from './utils/path-join.js'
const defaultReferencesCount = 16
const defaultCacheSize = 1000
+/**
+ * Creates an instance of Database.
+ * @function
+ * @param {Object} params One or more parameters for configuring Database.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {Identity} [params.identity] An Identity instance.
+ * @param {string} [params.address] The address of the database.
+ * @param {string} [params.name] The name of the database.
+ * @param {module:AccessControllers} [params.access] An AccessController
+ * instance.
+ * @param {string} [params.directory] A location for storing Database-related
+ * data. Defaults to ./orbitdb/[params.address].
+ * @param {*} [params.meta={}] The database's metadata.
+ * @param {module:Storage} [params.headsStorage] A compatible storage
+ * instance for storing log heads. Defaults to ComposedStorage.
+ * @param {module:Storage} [params.entryStorage] A compatible storage instance
+ * for storing log entries. Defaults to ComposedStorage.
+ * @param {module:Storage} [params.indexStorage] A compatible storage
+ * instance for storing an index of log entries. Defaults to ComposedStorage.
+ * @param {number} [params.referencesCount=16] The maximum distance between
+ * references to other entries.
+ * @param {boolean} [params.syncAutomatically=false] If true, sync databases
+ * automatically. Otherwise, false.
+ * @param {function} [params.onUpdate] A function callback. Fired when an
+ * entry is added to the oplog.
+ * @return {module:Database~Database} An instance of Database.
+ * @instance
+ */
const Database = async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
/**
* @namespace module:Database~Database
- * @description The instance returned by {@link module:Database}.
+ * @description The instance returned by {@link module:Database~Database}.
+ */
+
+ /**
+ * Event fired when an update occurs.
+ * @event module:Database~Database#update
+ * @param {module:Entry} entry An entry.
+ * @example
+ * database.events.on('update', (entry) => ...)
+ */
+
+ /**
+ * Event fired when a close occurs.
+ * @event module:Database~Database#close
+ * @example
+ * database.events.on('close', () => ...)
+ */
+
+ /**
+ * Event fired when a drop occurs.
+ * @event module:Database~Database#drop
+ * @example
+ * database.events.on('drop', () => ...)
*/
directory = pathJoin(directory || './orbitdb', `./${address}/`)
@@ -36,9 +120,29 @@ const Database = async ({ ipfs, identity, address, name, access, directory, meta
const log = await Log(identity, { logId: address, access, entryStorage, headsStorage, indexStorage })
+ /**
+ * Event emitter that emits updates.
+ * @name events
+ * @†ype EventEmitter
+ * @fires update when an entry is added to the database.
+ * @fires close When the database is closed.
+ * @fires drop When the database is dropped.
+ * @memberof module:Database~Database
+ * @instance
+ */
const events = new EventEmitter()
+
const queue = new PQueue({ concurrency: 1 })
+ /**
+ * Adds an operation to the oplog.
+ * @function addOperation
+ * @param {*} op Some operation to add to the oplog.
+ * @return {string} The hash of the operation.
+ * @memberof module:Database~Database
+ * @instance
+ * @async
+ */
const addOperation = async (op) => {
const task = async () => {
const entry = await log.append(op, { referencesCount })
@@ -70,6 +174,12 @@ const Database = async ({ ipfs, identity, address, name, access, directory, meta
await queue.add(task)
}
+ /**
+ * Closes the database, stopping sync and closing the oplog.
+ * @memberof module:Database~Database
+ * @instance
+ * @async
+ */
const close = async () => {
await sync.stop()
await queue.onIdle()
@@ -77,15 +187,27 @@ const Database = async ({ ipfs, identity, address, name, access, directory, meta
events.emit('close')
}
+ /**
+ * Drops the database, clearing the oplog.
+ * @memberof module:Database~Database
+ * @instance
+ * @async
+ */
const drop = async () => {
await queue.onIdle()
await log.clear()
events.emit('drop')
}
- // Start the Sync protocol
- // Sync protocol exchanges OpLog heads (latest known entries) between peers when they connect
- // Sync emits 'join', 'leave' and 'error' events through the given event emitter
+ /**
+ * Starts the [Sync protocol]{@link module:Sync~Sync}.
+ *
+ * Sync protocol exchanges OpLog heads (latest known entries) between peers
+ * when they connect.
+ * @memberof module:Database~Database
+ * @instance
+ * @async
+ */
const sync = await Sync({ ipfs, log, events, onSynced: applyOperation, start: syncAutomatically })
return {
diff --git a/src/db/documents.js b/src/db/documents.js
index e21ec94..2780f0a 100644
--- a/src/db/documents.js
+++ b/src/db/documents.js
@@ -1,12 +1,62 @@
/**
* @namespace Database-Documents
* @memberof module:Database
- * @description Documents Database
+ * @description Documents database.
+ * @example
Create documents db with default options
+ * import { create } from 'IPFS'
+ *
+ * const ipfs = create()
+ * const Partial = Documents()
+ * const documents = await Partial({ ipfs })
+ * @example Create documents db with custom index
+ * import { create } from 'IPFS'
+ *
+ * const ipfs = create()
+ * const options = { indexBy: 'myCustomId'}
+ * const Partial = Documents(options)
+ * const documents = await Partial({ ipfs })
*/
import Database from '../database.js'
const DefaultOptions = { indexBy: '_id' }
+/**
+ * Creates an instance of Documents.
+ * @callback Documents
+ * @param {Object} params One or more parameters for configuring Database.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {Identity} [params.identity] An Identity instance.
+ * @param {string} [params.address] The address of the database.
+ * @param {string} [params.name] The name of the database.
+ * @param {module:AccessControllers} [params.access] An AccessController
+ * instance.
+ * @param {string} [params.directory] A location for storing Database-related
+ * data. Defaults to ./orbitdb/[params.address].
+ * @param {*} [params.meta={}] The database's metadata.
+ * @param {module:Storage} [params.headsStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log heads. Defaults to ComposedStorage(LRUStorage, IPFSBlockStorage).
+ * @param {module:Storage} [params.entryStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {module:Storage} [params.indexStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing an " index of log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {number} [params.referencesCount] The maximum distance between
+ * references to other entries.
+ * @param {boolean} [params.syncAutomatically=false] If true, sync databases
+ * automatically. Otherwise, false.
+ * @param {function} [params.onUpdate] A function callback. Fired when an
+ * entry is added to the oplog.
+ * @function
+ * @instance
+ * @async
+ * @memberof module:Database.Database-Documents
+ */
+
+/**
+ * Defines a Documents database.
+ * @param {Object} options Various options for configuring the Document store.
+ * @param {string} [params.indexBy=_id] An index.
+ * @returns {module:Database.Database-Documents} A Documents function.
+ * @memberof module:Database
+ */
const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
const database = await Database({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically })
@@ -14,9 +64,11 @@ const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, add
/**
* Stores a document to the store.
- *
+ * @function
* @param {Object} doc An object representing a key/value list of fields.
* @returns {string} The hash of the new oplog entry.
+ * @memberof module:Database.Database-Documents
+ * @instance
*/
const put = async (doc) => {
const key = doc[indexBy]
@@ -28,9 +80,11 @@ const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, add
/**
* Deletes a document from the store.
- *
+ * @function
* @param {string} key The key of the doc to delete.
* @returns {string} The hash of the new oplog entry.
+ * @memberof module:Database.Database-Documents
+ * @instance
*/
const del = async (key) => {
if (!await get(key)) { throw new Error(`No document with key '${key}' in the database`) }
@@ -40,9 +94,11 @@ const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, add
/**
* Gets a document from the store by key.
- *
+ * @function
* @param {string} key The key of the doc to get.
* @returns {Object} The doc corresponding to key or null.
+ * @memberof module:Database.Database-Documents
+ * @instance
*/
const get = async (key) => {
for await (const doc of iterator()) {
@@ -54,9 +110,12 @@ const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, add
/**
* Queries the document store for documents matching mapper filters.
- *
- * @param {function(Object)} findFn A function for querying for specific results.
+ * @function
+ * @param {function(Object)} findFn A function for querying for specific
+ * results.
* @returns {Array} Found documents.
+ * @memberof module:Database.Database-Documents
+ * @instance
*/
const query = async (findFn) => {
const results = []
@@ -70,6 +129,15 @@ const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, add
return results
}
+ /**
+ * Iterates over documents.
+ * @function
+ * @params {Object} [filters={}] Various filters to apply to the iterator.
+ * @params {string} [filters.amount=-1] The number of results to fetch.
+ * @yields [string, string, string] The next document as hash/key/value.
+ * @memberof module:Database.Database-Documents
+ * @instance
+ */
const iterator = async function * ({ amount } = {}) {
const keys = {}
let count = 0
@@ -89,6 +157,14 @@ const Documents = ({ indexBy } = DefaultOptions) => async ({ ipfs, identity, add
}
}
+ /**
+ * Returns all documents.
+ * @function
+ * @returns [][string, string, string] An array of documents as hash/key
+ * value entries.
+ * @memberof module:Database.Database-Documents
+ * @instance
+ */
const all = async () => {
const values = []
for await (const entry of iterator()) {
diff --git a/src/db/events.js b/src/db/events.js
index 85f193a..35d56f6 100644
--- a/src/db/events.js
+++ b/src/db/events.js
@@ -1,24 +1,92 @@
/**
* @namespace Database-Events
* @memberof module:Database
- * @description Events Database
+ * @description Events database.
*/
import Database from '../database.js'
+/**
+ * Creates an instance of Events.
+ * @callback Events
+ * @param {Object} params One or more parameters for configuring Database.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {Identity} [params.identity] An Identity instance.
+ * @param {string} [params.address] The address of the database.
+ * @param {string} [params.name] The name of the database.
+ * @param {module:AccessControllers} [params.access] An AccessController
+ * instance.
+ * @param {string} [params.directory] A location for storing Database-related
+ * data. Defaults to ./orbitdb/[params.address].
+ * @param {*} [params.meta={}] The database's metadata.
+ * @param {module:Storage} [params.headsStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log heads. Defaults to ComposedStorage(LRUStorage, IPFSBlockStorage).
+ * @param {module:Storage} [params.entryStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {module:Storage} [params.indexStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing an " index of log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {number} [params.referencesCount] The maximum distance between
+ * references to other entries.
+ * @param {boolean} [params.syncAutomatically=false] If true, sync databases
+ * automatically. Otherwise, false.
+ * @param {function} [params.onUpdate] A function callback. Fired when an
+ * entry is added to the oplog.
+ * @function
+ * @instance
+ * @async
+ * @memberof module:Database.Database-Events
+ */
+
+/**
+ * Defines an Events database.
+ * @returns {module:Database.Database-Events} A Events function.
+ * @memberof module:Database
+ */
const Events = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
const database = await Database({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate })
const { addOperation, log } = database
+ /**
+ * Adds an event to the store.
+ * @function
+ * @param {*} value The event to be added.
+ * @returns {string} The hash of the new oplog entry.
+ * @memberof module:Database.Database-Events
+ * @instance
+ */
const add = async (value) => {
return addOperation({ op: 'ADD', key: null, value })
}
+ /**
+ * Gets an event from the store by hash.
+ * @function
+ * @param {string} hash The hash of the event to get.
+ * @returns {*} The value corresponding to hash or null.
+ * @memberof module:Database.Database-Events
+ * @instance
+ */
const get = async (hash) => {
const entry = await log.get(hash)
return entry.payload.value
}
+ /**
+ * Iterates over events.
+ * @function
+ * @params {Object} [filters={}] Various filters to apply to the iterator.
+ * @params {string} [filters.gt] All events which are greater than the
+ * given hash.
+ * @params {string} [filters.gte] All events which are greater than or equal
+ * to the given hash.
+ * @params {string} [filters.lt] All events which are less than the given
+ * hash.
+ * @params {string} [filters.lte] All events which are less than or equal to
+ * the given hash.
+ * @params {string} [filters.amount=-1] The number of results to fetch.
+ * @yields [string, string] The next event as hash/value.
+ * @memberof module:Database.Database-Events
+ * @instance
+ */
const iterator = async function * ({ gt, gte, lt, lte, amount } = {}) {
const it = log.iterator({ gt, gte, lt, lte, amount })
for await (const event of it) {
@@ -28,6 +96,13 @@ const Events = () => async ({ ipfs, identity, address, name, access, directory,
}
}
+ /**
+ * Returns all events.
+ * @function
+ * @returns [][string, string] An array of events as hash/value entries.
+ * @memberof module:Database.Database-Events
+ * @instance
+ */
const all = async () => {
const values = []
for await (const entry of iterator()) {
diff --git a/src/db/keyvalue-indexed.js b/src/db/keyvalue-indexed.js
index d6a6ec2..3c0716c 100644
--- a/src/db/keyvalue-indexed.js
+++ b/src/db/keyvalue-indexed.js
@@ -1,7 +1,17 @@
/**
* @namespace Database-KeyValueIndexed
* @memberof module:Database
- * @description KeyValueIndexed Database
+ * @description
+ * KeyValueIndexed database.
+ *
+ * Key/value pairs are stored to the configured storage.
+ * @example Specify a custom storage
+ * import { create } from 'IPFS'
+ *
+ * const ipfs = create()
+ * const storage = await IPFSBlockStorage()
+ * const Partial = KeyValueIndexed({ storage })
+ * const keyValueIndexed = await Partial({ ipfs })
*/
import { KeyValue } from './index.js'
import LevelStorage from '../storage/level.js'
@@ -9,6 +19,45 @@ import pathJoin from '../utils/path-join.js'
const valueEncoding = 'json'
+/**
+ * Creates an instance of KeyValueIndexed.
+ * @callback KeyValueIndexed
+ * @param {Object} params One or more parameters for configuring Database.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {Identity} [params.identity] An Identity instance.
+ * @param {string} [params.address] The address of the database.
+ * @param {string} [params.name] The name of the database.
+ * @param {module:AccessControllers} [params.access] An AccessController
+ * instance.
+ * @param {string} [params.directory] A location for storing Database-related
+ * data. Defaults to ./orbitdb/[params.address].
+ * @param {*} [params.meta={}] The database's metadata.
+ * @param {module:Storage} [params.headsStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log heads. Defaults to ComposedStorage(LRUStorage, IPFSBlockStorage).
+ * @param {module:Storage} [params.entryStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {module:Storage} [params.indexStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing an " index of log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {number} [params.referencesCount] The maximum distance between
+ * references to other entries.
+ * @param {boolean} [params.syncAutomatically=false] If true, sync databases
+ * automatically. Otherwise, false.
+ * @param {function} [params.onUpdate] A function callback. Fired when an
+ * entry is added to the oplog.
+ * @function
+ * @instance
+ * @async
+ * @memberof module:Database.Database-KeyValueIndexed
+ */
+
+/**
+ * Defines a KeyValueIndexed database.
+ * @param {Object} options Various options for configuring the KeyValueIndexed
+ * store.
+ * @param {module:Storage} [storage=LevelStorage] A compatible storage.
+ * @returns {module:Database.Database-KeyValueIndexed} A KeyValueIndexed
+ * function.
+ * @memberof module:Database
+ */
const KeyValueIndexed = ({ storage } = {}) => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
const indexDirectory = pathJoin(directory || './orbitdb', `./${address}/_index/`)
const index = storage || await LevelStorage({ path: indexDirectory, valueEncoding })
@@ -40,6 +89,14 @@ const KeyValueIndexed = ({ storage } = {}) => async ({ ipfs, identity, address,
// Compute the index
await _updateIndex(keyValueStore.log)
+ /**
+ * Gets a value from the store by key.
+ * @function
+ * @param {string} key The key of the value to get.
+ * @returns {*} The value corresponding to key or null.
+ * @memberof module:Database.Database-KeyValueIndexed
+ * @instance
+ */
const get = async (key) => {
const value = await index.get(key)
if (value) {
@@ -48,6 +105,15 @@ const KeyValueIndexed = ({ storage } = {}) => async ({ ipfs, identity, address,
return keyValueStore.get(key)
}
+ /**
+ * Iterates over keyvalue pairs.
+ * @function
+ * @params {Object} [filters={}] Various filters to apply to the iterator.
+ * @params {string} [filters.amount=-1] The number of results to fetch.
+ * @yields [string, string, string] The next key/value as key/value/hash.
+ * @memberof module:Database.Database-KeyValueIndexed
+ * @instance
+ */
const iterator = async function * ({ amount } = {}) {
const it = keyValueStore.iterator({ amount })
for await (const { key, value, hash } of it) {
@@ -55,11 +121,17 @@ const KeyValueIndexed = ({ storage } = {}) => async ({ ipfs, identity, address,
}
}
+ /**
+ * Closes the index and underlying storage.
+ */
const close = async () => {
await index.close()
await keyValueStore.close()
}
+ /**
+ * Drops all records from the index and underlying storage.
+ */
const drop = async () => {
await index.clear()
await keyValueStore.drop()
diff --git a/src/db/keyvalue.js b/src/db/keyvalue.js
index b8b8962..7df4f1c 100644
--- a/src/db/keyvalue.js
+++ b/src/db/keyvalue.js
@@ -1,23 +1,82 @@
/**
* @namespace Database-KeyValue
* @memberof module:Database
- * @description KeyValue Database
+ * @description KeyValue database.
*/
import Database from '../database.js'
+/**
+ * Creates an instance of KeyValue.
+ * @callback KeyValue
+ * @param {Object} params One or more parameters for configuring Database.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {Identity} [params.identity] An Identity instance.
+ * @param {string} [params.address] The address of the database.
+ * @param {string} [params.name] The name of the database.
+ * @param {module:AccessControllers} [params.access] An AccessController
+ * instance.
+ * @param {string} [params.directory] A location for storing Database-related
+ * data. Defaults to ./orbitdb/[params.address].
+ * @param {*} [params.meta={}] The database's metadata.
+ * @param {module:Storage} [params.headsStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log heads. Defaults to ComposedStorage(LRUStorage, IPFSBlockStorage).
+ * @param {module:Storage} [params.entryStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {module:Storage} [params.indexStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing an " index of log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {number} [params.referencesCount] The maximum distance between
+ * references to other entries.
+ * @param {boolean} [params.syncAutomatically=false] If true, sync databases
+ * automatically. Otherwise, false.
+ * @param {function} [params.onUpdate] A function callback. Fired when an
+ * entry is added to the oplog.
+ * @function
+ * @instance
+ * @async
+ * @memberof module:Database.Database-KeyValue
+ */
+
+/**
+ * Defines an KeyValue database.
+ * @returns {module:Database.Database-KeyValue} A KeyValue function.
+ * @memberof module:Database
+ */
const KeyValue = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
const database = await Database({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate })
const { addOperation, log } = database
+ /**
+ * Stores a key/value pair to the store.
+ * @function
+ * @param {string} key The key to store.
+ * @param {*} value The value to store.
+ * @returns {string} The hash of the new oplog entry.
+ * @memberof module:Database.Database-KeyValue
+ * @instance
+ */
const put = async (key, value) => {
return addOperation({ op: 'PUT', key, value })
}
+ /**
+ * Deletes a key/value pair from the store.
+ * @function
+ * @param {string} key The key of the key/value pair to delete.
+ * @memberof module:Database.Database-KeyValue
+ * @instance
+ */
const del = async (key) => {
return addOperation({ op: 'DEL', key, value: null })
}
+ /**
+ * Gets a value from the store by key.
+ * @function
+ * @param {string} key The key of the value to get.
+ * @returns {*} The value corresponding to key or null.
+ * @memberof module:Database.Database-KeyValue
+ * @instance
+ */
const get = async (key) => {
for await (const entry of log.traverse()) {
const { op, key: k, value } = entry.payload
@@ -29,6 +88,15 @@ const KeyValue = () => async ({ ipfs, identity, address, name, access, directory
}
}
+ /**
+ * Iterates over keyvalue pairs.
+ * @function
+ * @params {Object} [filters={}] Various filters to apply to the iterator.
+ * @params {string} [filters.amount=-1] The number of results to fetch.
+ * @yields [string, string, string] The next key/value as key/value/hash.
+ * @memberof module:Database.Database-KeyValue
+ * @instance
+ */
const iterator = async function * ({ amount } = {}) {
const keys = {}
let count = 0
@@ -48,6 +116,14 @@ const KeyValue = () => async ({ ipfs, identity, address, name, access, directory
}
}
+ /**
+ * Returns all key/value pairs.
+ * @function
+ * @returns [][string, string, string] An array of key/value pairs as
+ * key/value/hash entries.
+ * @memberof module:Database.Database-KeyValue
+ * @instance
+ */
const all = async () => {
const values = []
for await (const entry of iterator()) {
diff --git a/src/identities/identities.js b/src/identities/identities.js
index 50e5ce5..b0bf19f 100644
--- a/src/identities/identities.js
+++ b/src/identities/identities.js
@@ -1,4 +1,10 @@
-/** @module Identities */
+/**
+ * @module Identities
+ * @description
+ * Identities provides a framework for generating and managing identity
+ * details and providers.
+ */
+
import Identity, { isIdentity, isEqual, decodeIdentity } from './identity.js'
import { PublicKeyIdentityProvider } from './providers/index.js'
// import DIDIdentityProvider from './identity-providers/did.js'
@@ -16,7 +22,29 @@ const supportedTypes = {
// [EthIdentityProvider.type]: EthIdentityProvider
}
+/**
+ * Creates an instance of Identities.
+ * @function
+ * @param {Object} params One or more parameters for configuring Identities.
+ * @param {module:KeyStore} [params.keystore] A preconfigured KeyStore.
+ * A KeyStore will be created in the path defined by the path param. If neither
+ * Keystore nor path are defined, a new KeyStore is stored in ./orbitdb
+ * identities.
+ * @param {string} [params.path] The path to a KeyStore. If no path is
+ * provided, the default is ./orbitdb/identities.
+ * @param {module:Storage} [params.storage] An instance of a compatible storage
+ * module.
+ * @param {IPFS} [params.ipfs] An instance of IPFS. This param is not required
+ * if storage is provided.
+ * @returns {module:Identities~Identities} An instance of Identities.
+ * @instance
+ */
const Identities = async ({ keystore, path, storage, ipfs } = {}) => {
+ /**
+ * @namespace module:Identities~Identities
+ * @description The instance returned by {@link module:Identities~Identities}.
+ */
+
keystore = keystore || await KeyStore({ path: path || DefaultIdentityKeysPath })
if (!storage) {
@@ -27,6 +55,13 @@ const Identities = async ({ keystore, path, storage, ipfs } = {}) => {
const verifiedIdentitiesCache = await LRUStorage({ size: 1000 })
+ /**
+ * Gets an identity by hash.
+ * @param {string} hash An identity hash.
+ * @returns {Identity} An instance of identity.
+ * @memberof module:Identities~Identities
+ * @instance
+ */
const getIdentity = async (hash) => {
const bytes = await storage.get(hash)
if (bytes) {
@@ -34,6 +69,14 @@ const Identities = async ({ keystore, path, storage, ipfs } = {}) => {
}
}
+ /**
+ * Creates an identity, adding it to storage.
+ * @param {Object} options Various options for configuring a new identity.
+ * @param {string} [options.type=PublicKeyIdentityProvider.type] The type of provider to use for generating an identity.
+ * @returns {Identity} An instance of identity.
+ * @memberof module:Identities~Identities
+ * @instance
+ */
const createIdentity = async (options = {}) => {
options.keystore = keystore
@@ -57,6 +100,12 @@ const Identities = async ({ keystore, path, storage, ipfs } = {}) => {
return identity
}
+ /**
+ * Verifies an identity using the identity's provider.
+ * @param {Identity} identity The identity to verify.
+ * @returns {boolean} True the identity is valid, false otherwise.
+ * @memberof module:Identities~Identities
+ */
const verifyIdentity = async (identity) => {
if (!isIdentity(identity)) {
return false
@@ -84,6 +133,16 @@ const Identities = async ({ keystore, path, storage, ipfs } = {}) => {
return identityVerified
}
+ /**
+ * Signs data using an identity.
+ * @param {Identity} identity The identity to use for signing.
+ * @param {string} data The data to sign.
+ * @returns {string} The signed data.
+ * @throws Private signing key not fund from KeyStore when no signing key can
+ * be retrieved.
+ * @memberof module:Identities~Identities
+ * @instance
+ */
const sign = async (identity, data) => {
const signingKey = await keystore.getKey(identity.id)
@@ -108,10 +167,26 @@ const Identities = async ({ keystore, path, storage, ipfs } = {}) => {
}
}
+/**
+ * Checks whether an identity provider is supported.
+ * @param {string} type The identity provider type.
+ * @returns {boolean} True if the identity provider is supported, false
+ * otherwise.
+ * @static
+ */
const isProviderSupported = (type) => {
return Object.keys(supportedTypes).includes(type)
}
+/**
+ * Gets an identity provider.
+ * @param {string} type The identity provider type.
+ * @returns {IdentityProvider} The IdentityProvider module corresponding to
+ * type.
+ * @throws IdentityProvider type is not supported if the identity provider is
+ * not supported.
+ * @static
+ */
const getProviderFor = (type) => {
if (!isProviderSupported(type)) {
throw new Error(`IdentityProvider type '${type}' is not supported`)
@@ -120,6 +195,15 @@ const getProviderFor = (type) => {
return supportedTypes[type]
}
+/**
+ * Adds an identity provider.
+ * @param {IdentityProvider} IdentityProvider The identity provider to add.
+ * @throws IdentityProvider must be given as an argument if no module is
+ * provided.
+ * @throws 'Given IdentityProvider doesn't have a field 'type' if the
+ * IdentityProvider does not include a type property.
+ * @static
+ */
const addIdentityProvider = (IdentityProvider) => {
if (!IdentityProvider) {
throw new Error('IdentityProvider must be given as an argument')
@@ -133,6 +217,11 @@ const addIdentityProvider = (IdentityProvider) => {
supportedTypes[IdentityProvider.type] = IdentityProvider
}
+/**
+ * Removes an identity provider.
+ * @param {string} type The identity provider type.
+ * @static
+ */
const removeIdentityProvider = (type) => {
delete supportedTypes[type]
}
diff --git a/src/identities/identity.js b/src/identities/identity.js
index de4c698..9596a9e 100644
--- a/src/identities/identity.js
+++ b/src/identities/identity.js
@@ -1,3 +1,9 @@
+/**
+ * @module Identity
+ * @description
+ * An identity.
+ */
+
import * as Block from 'multiformats/block'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
@@ -7,6 +13,26 @@ const codec = dagCbor
const hasher = sha256
const hashStringEncoding = base58btc
+/**
+ * Creates an instance of Identity.
+ * @function
+ * @param {Object} params One or more parameters for configuring an Identity.
+ * @param {string} params.id A unique identitifer for the identity.
+ * @param {string} params.publicKey A public key.
+ * @param {Object} params.signatures A signed identity id and public key.
+ * @param {string} params.type The type of identity provider.
+ * @param {function} params.sign A sign function.
+ * @param {function} params.verify A verify function.
+ * @returns {module:Identity~Identity} An instance of Identity.
+ * @throws Identity id is required if id is not provided.
+ * @throws Invalid public key if publicKey is not provided.
+ * @throws Signatures object is required if signature is not provided.
+ * @throws Signature of id is required if signature's id is not provided.
+ * @throws Signature of publicKey+id is required if signature's publicKey+id is
+ * not provided.
+ * @throws Identity type is required if type is not provided.
+ * @instance
+ */
const Identity = async ({ id, publicKey, signatures, type, sign, verify } = {}) => {
if (id == null) throw new Error('Identity id is required')
if (publicKey == null) throw new Error('Invalid public key')
@@ -34,9 +60,10 @@ const Identity = async ({ id, publicKey, signatures, type, sign, verify } = {})
}
/**
- * Encode an Identity to a serializable form
- * @param {Identity} identity Identity to encode
- * @returns {Object} Object with fields hash and bytes
+ * Encode an Identity to a serializable form.
+ * @param {Identity} identity Identity to encode,
+ * @returns {Object} Object with fields hash and bytes.
+ * @static
*/
const _encodeIdentity = async (identity) => {
const { id, publicKey, signatures, type } = identity
@@ -50,12 +77,19 @@ const _encodeIdentity = async (identity) => {
* Decode an Identity from bytes
* @param {Uint8Array} bytes Bytes from which to decode an Identity from
* @returns {Identity}
+ * @static
*/
const decodeIdentity = async (bytes) => {
const { value } = await Block.decode({ bytes, codec, hasher })
return Identity({ ...value })
}
+/**
+ * Verifies whether an identity is valid.
+ * @param {Identity} identity The identity to verify.
+ * @return {boolean} True if the identity is valid, false otherwise.
+ * @static
+ */
const isIdentity = (identity) => {
return identity.id != null &&
identity.hash != null &&
@@ -67,6 +101,13 @@ const isIdentity = (identity) => {
identity.type != null
}
+/**
+ * Evaluates whether two identities are equal.
+ * @param {Identity} a First identity.
+ * @param {Identity} b Second identity.
+ * @return {boolean} True if identity a and b are equal, false otherwise.
+ * @static
+ */
const isEqual = (a, b) => {
return a.id === b.id &&
a.hash === b.hash &&
diff --git a/src/key-store.js b/src/key-store.js
index 91d50e7..04c3254 100644
--- a/src/key-store.js
+++ b/src/key-store.js
@@ -1,3 +1,13 @@
+/**
+* @module KeyStore
+* @description
+* Provides a local key manager for OrbitDB.
+* @example Create a keystore with defaults.
+* const keystore = await KeyStore()
+* @example Create a keystore with custom storage.
+* const storage = await MemoryStorage()
+* const keystore = await KeyStore({ storage })
+*/
import * as crypto from '@libp2p/crypto'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
@@ -9,6 +19,17 @@ import LRUStorage from './storage/lru.js'
const unmarshal = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey
const unmarshalPubKey = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PublicKey
+/**
+ * Verifies a signature used for signing data.
+ * @params {string} signature The generated signature.
+ * @params {string} publicKey The derived public key of the key pair.
+ * @params {string} data The data to be verified.
+ * @return {boolean} True if the signature is valid, false otherwise.
+ * @throws No signature given if no signature is provided.
+ * @throws Given publicKey was undefined if no publicKey is provided.
+ * @throws Given input data was undefined if no data is provided.
+ * @static
+ */
const verifySignature = async (signature, publicKey, data) => {
if (!signature) {
throw new Error('No signature given')
@@ -37,6 +58,15 @@ const verifySignature = async (signature, publicKey, data) => {
return Promise.resolve(res)
}
+/**
+ * Signs data using a key pair.
+ * @params {string} key The key to use for signing data.
+ * @params {string} data The data to sign.
+ * @return {string} A signature.
+ * @throws No signing key given if no key is provided.
+ * @throws Given input data was undefined if no data is provided.
+ * @static
+ */
const signMessage = async (key, data) => {
if (!key) {
throw new Error('No signing key given')
@@ -55,6 +85,14 @@ const signMessage = async (key, data) => {
const verifiedCachePromise = LRUStorage({ size: 1000 })
+/**
+ * Verifies input data against a cached version of the signed message.
+ * @params {string} signature The generated signature.
+ * @params {string} publicKey The derived public key of the key pair.
+ * @params {string} data The data to be verified.
+ * @return {boolean} True if the the data and cache match, false otherwise.
+ * @static
+ */
const verifyMessage = async (signature, publicKey, data) => {
const verifiedCache = await verifiedCachePromise
const cached = await verifiedCache.get(signature)
@@ -81,22 +119,50 @@ const defaultPath = './keystore'
/**
* Creates an instance of KeyStore.
- * @param {Object} options Various options to use when instantiating KeyStore.
- * @param {Object} options.storage An instance of a storage class. Can be one of ComposedStorage, IPFSBlockStorage, LevelStorage, etc. Defaults to ComposedStorage.
- * @param {string} options.path The path to a valid storage. Defaults to ./keystore.
- * @return {KeyStore} An instance of KeyStore.
+ * @param {Object} params One or more parameters for configuring KeyStore.
+ * @param {Object} [params.storage] An instance of a storage class. Can be one
+ * of ComposedStorage, IPFSBlockStorage, LevelStorage, etc. Defaults to
+ * ComposedStorage.
+ * @param {string} [params.path=./keystore] The path to a valid storage.
+ * @return {module:KeyStore~KeyStore} An instance of KeyStore.
+ * @instance
*/
const KeyStore = async ({ storage, path } = {}) => {
+ /**
+ * @namespace module:KeyStore~KeyStore
+ * @description The instance returned by {@link module:KeyStore}.
+ */
storage = storage || await ComposedStorage(await LRUStorage({ size: 1000 }), await LevelStorage({ path: path || defaultPath }))
+ /**
+ * Closes the KeyStore's underlying storage.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
const close = async () => {
await storage.close()
}
+ /**
+ * Clears the KeyStore's underlying storage.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
const clear = async () => {
await storage.clear()
}
+ /**
+ * Checks if the key exists in the key store.
+ * @param {string} id The id of the private key in the key store.
+ * @return {boolean} True if the key exists, false otherwise.
+ * @throws id needed to check a key if no id is specified.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
const hasKey = async (id) => {
if (!id) {
throw new Error('id needed to check a key')
@@ -114,12 +180,27 @@ const KeyStore = async ({ storage, path } = {}) => {
return hasKey
}
+ /**
+ * Adds a key to the keystore.
+ * @param {string} id A storage id for the key.
+ * @param {Uint8Array} key The key to store.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
const addKey = async (id, key) => {
- // await storage.put('public_' + id, key.publicKey)
await storage.put('private_' + id, key.privateKey)
}
- const createKey = async (id, { entropy } = {}) => {
+ /**
+ * Creates a key, storing it to the keystore.
+ * @param {string} id A storage id for the key.
+ * @throws id needed to create a key if no id is specified.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
+ const createKey = async (id) => {
if (!id) {
throw new Error('id needed to create a key')
}
@@ -139,6 +220,15 @@ const KeyStore = async ({ storage, path } = {}) => {
return keys
}
+ /**
+ * Gets the key from keystore.
+ * @param {string} id A storage id of the key.
+ * @return {Uint8Array} The key specified by id.
+ * @throws id needed to get a key if no id is specified.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
const getKey = async (id) => {
if (!id) {
throw new Error('id needed to get a key')
@@ -158,6 +248,19 @@ const KeyStore = async ({ storage, path } = {}) => {
return unmarshal(storedKey)
}
+ /**
+ * Gets th serialized public key from a key pair.
+ * @param {*} keys A key pair.
+ * @param {Object} options One or more options.
+ * @param {Object} [options.format=hex] The format the public key should be
+ * returned in.
+ * @return {Uint8Array|String} The public key.
+ * @throws Supported formats are `hex` and `buffer` if an invalid format is
+ * passed in options.
+ * @memberof module:KeyStore~KeyStore
+ * @async
+ * @instance
+ */
const getPublic = (keys, options = {}) => {
const formats = ['hex', 'buffer']
const format = options.format || 'hex'
diff --git a/src/manifest.js b/src/manifest.js
index b4308b1..d389673 100644
--- a/src/manifest.js
+++ b/src/manifest.js
@@ -1,4 +1,9 @@
-/** @namespace Manifest */
+/**
+ * @module Manifest
+ * @description
+ * A manifest provides an OrbitDB database with various descriptive information
+ * including access controls and metadata.
+ */
import * as Block from 'multiformats/block'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
@@ -9,19 +14,51 @@ const codec = dagCbor
const hasher = sha256
const hashStringEncoding = base58btc
-// Creates a DB manifest file and saves it in IPFS
+/**
+ * Creates a DB manifest file and saves it in IPFS.
+ * @function
+ * @param {Object} params One or more parameters for configuring Manifest.
+ * @param {IPFS} params.ipfs An instance of IPFS.
+ * @param {module:Storage} [param.storage=module:Storage.Storage-ComposedStorage] An instance of Storage.
+ * @returns {module:Manifest} An instance of Manifest.
+ * @instance
+ */
const Manifest = async ({ ipfs, storage } = {}) => {
+ /**
+ * @namespace module:Manifest~Manifest
+ * @description The instance returned by {@link module:Manifest~Manifest}.
+ */
+
storage = storage || await ComposedStorage(
await LRUStorage({ size: 1000 }),
await IPFSBlockStorage({ ipfs, pin: true })
)
+ /**
+ * Gets the manifest data from the underlying storage.
+ * @param {string} address The address of the manifest.
+ * @returns {*} The manifest data.
+ * @memberof module:Manifest~Manifest
+ */
const get = async (address) => {
const bytes = await storage.get(address)
const { value } = await Block.decode({ bytes, codec, hasher })
return value
}
+ /**
+ * Creates a valid manifest.
+ * @param {Object} params One or more parameters for configuring Manifest.
+ * @param {string} name The name of the database.
+ * @param {string} type The type of the database.
+ * @param {string} accessController The type of access controller.
+ * @param {Object} meta Metadata.
+ * @returns {Object} A hash and manifest.
+ * @throws name is required if no name is provided.
+ * @throws type is required if no type is provided.
+ * @throws accessController is required if no access controller is provided.
+ * @memberof module:Manifest~Manifest
+ */
const create = async ({ name, type, accessController, meta }) => {
if (!name) throw new Error('name is required')
if (!type) throw new Error('type is required')
@@ -47,6 +84,10 @@ const Manifest = async ({ ipfs, storage } = {}) => {
}
}
+ /**
+ * Closes the underlying storage.
+ * @memberof module:Manifest~Manifest
+ */
const close = async () => {
await storage.close()
}
diff --git a/src/oplog/log.js b/src/oplog/log.js
index 915a618..730648f 100644
--- a/src/oplog/log.js
+++ b/src/oplog/log.js
@@ -144,7 +144,7 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
*
* @param {data} data Payload to add to the entry
* @param {Object} options
- * @param {Integer} options.referencesCount TODO
+ * @param {number} options.referencesCount TODO
* @return {module:Log~Entry} Entry that was appended
* @memberof module:Log~Log
* @instance
diff --git a/src/orbitdb.js b/src/orbitdb.js
index 4d2e3e3..0bad08d 100644
--- a/src/orbitdb.js
+++ b/src/orbitdb.js
@@ -1,4 +1,33 @@
-/** @module OrbitDB */
+/**
+* @module OrbitDB
+* @description
+* OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses
+* IPFS as its data storage and Libp2p Pubsub to automatically sync databases
+* with peers. It's an eventually consistent database that uses Merkle-CRDTs
+* for conflict-free database writes and merges making OrbitDB an excellent
+* choice for p2p and decentralized apps, blockchain applications and local
+* first web applications.
+*
+* To install OrbitDB:
+* ```bash
+* npm install orbit-db
+* ```
+*
+* IPFS is also required:
+* ```bash
+* npm install ipfs-core
+* ```
+* @example Instantiate OrbitDB and open a new database:
+* import { create } from 'ipfs-core'
+* import OrbitDB from 'orbit-db'
+*
+* const ipfs = await create() // IPFS is required for storage and syncing
+* const orbitdb = await OrbitDB({ ipfs })
+* const mydb = await orbitdb.open('mydb')
+* const dbAddress = mydb.address // E.g. /orbitdb/zdpuAuK3BHpS7NvMBivynypqciYCuy2UW77XYBPUYRnLjnw13
+* @example Open an existing database using its multiformat address:
+* const mydb = await orbitdb.open(dbAddress)
+*/
import { Events, KeyValue, Documents } from './db/index.js'
import KeyStore from './key-store.js'
import { Identities } from './identities/index.js'
@@ -9,13 +38,33 @@ import pathJoin from './utils/path-join.js'
import * as AccessControllers from './access-controllers/index.js'
import IPFSAccessController from './access-controllers/ipfs.js'
-// Mapping for database types
+/**
+ * An array of available database types.
+ * @name databaseTypes
+ * @†ype []
+ * @return [] An array of database types.
+ * @memberof module:OrbitDB
+ */
const databaseTypes = {
events: Events,
documents: Documents,
keyvalue: KeyValue
}
+/**
+ * Add a new database type.
+ * @example
+ * import { addDatabaseType } from 'orbit-db'
+ * const CustomDBTypeModule = async (params) => {
+ * const database = await Database(...params)
+ * ...
+ * }
+ * addDatabaseType('customDBType', CustomDBTypeModule)
+ * @function addDatabaseType
+ * @param {string} type The database type.
+ * @param {module:Database} store A Database-compatible module.
+ * @memberof module:OrbitDB
+ */
const addDatabaseType = (type, store) => {
if (databaseTypes[type]) {
throw new Error(`Type already exists: ${type}`)
@@ -24,11 +73,31 @@ const addDatabaseType = (type, store) => {
}
const DefaultDatabaseType = 'events'
+
const DefaultAccessController = IPFSAccessController
+/**
+ * Creates an instance of OrbitDB.
+ * @function
+ * @param {Object} params One or more parameters for configuring OrbitDB.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {string} [params.id] The id of the OrbitDB instance.
+ * @param {Identity} [params.identity] An Identity instance.
+ * @param {namespace:KeyStore} [params.keystore] A KeyStore instance.
+ * @param {string} [params.directory] A location for storing OrbitDB-related
+ * data.
+ * @return {module:OrbitDB~OrbitDB} An instance of OrbitDB.
+ * @throws IPFSinstance is required argument if no IPFS instance is provided.
+ * @instance
+ */
const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
+ /**
+ * @namespace module:OrbitDB~OrbitDB
+ * @description The instance returned by {@link module:OrbitDB}.
+ */
+
if (ipfs == null) {
- throw new Error('IPFS instance is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
+ throw new Error('IPFS instance is a required argument.')
}
id = id || await createId()
@@ -42,6 +111,46 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
let databases = {}
+ /**
+ * Open a database or create one if it does not already exist.
+ *
+ * By default, OrbitDB will create a database of type [DefaultDatabaseType]{@link module:OrbitDB~DefaultDatabaseType}:
+ * ```
+ * const mydb = await orbitdb.open('mydb')
+ * ```
+ * To create a database of a different type, specify the type param:
+ * ```
+ * const mydb = await orbitdb.open('mydb', {type: 'documents'})
+ * ```
+ * The type must be listed in [databaseTypes]{@link module:OrbitDB.databaseTypes} or an error is thrown.
+ * @function
+ * @param {string} address The address of an existing database to open, or
+ * the name of a new database.
+ * @param {Object} params One or more database configuration parameters.
+ * @param {string} [params.type=events] The database's type.
+ * @param {*} [params.meta={}] The database's metadata.
+ * @param {boolean} [params.sync=false] If true, sync databases automatically.
+ * Otherwise, false.
+ * @param {module:Database} [params.Database=[Events]{@link module:Database.Database-Events}] A Database-compatible
+ * module.
+ * @param {module:AccessControllers}
+ * [params.AccessController=IPFSAccessController]
+ * An AccessController-compatible module.
+ * @param {module:Storage} [params.headsStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log heads. Defaults to ComposedStorage(LRUStorage, IPFSBlockStorage).
+ * @param {module:Storage} [params.entryStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing
+ * log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {module:Storage} [params.indexStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing an " index of log entries. Defaults to ComposedStorage(LRUStorage, LevelStorage).
+ * @param {number} [params.referencesCount] The maximum distance between
+ * references to other entries.
+ * @memberof module:OrbitDB
+ * @return {module:Database} A database instance.
+ * @throws Unsupported database type if the type specified is not in the list
+ * of known databaseTypes.
+ * @memberof module:OrbitDB~OrbitDB
+ * @instance
+ * @async
+ */
const open = async (address, { type, meta, sync, Database, AccessController, headsStorage, entryStorage, indexStorage, referencesCount } = {}) => {
let name, manifest, accessController
@@ -97,6 +206,13 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
delete databases[address]
}
+ /**
+ * Stops OrbitDB, closing the underlying keystore and manifest.
+ * @function stop
+ * @memberof module:OrbitDB~OrbitDB
+ * @instance
+ * @async
+ */
const stop = async () => {
if (keystore) {
await keystore.close()
diff --git a/src/storage/composed.js b/src/storage/composed.js
index 6cf8a4f..a382842 100644
--- a/src/storage/composed.js
+++ b/src/storage/composed.js
@@ -1,18 +1,51 @@
/**
* @namespace Storage-Composed
* @memberof module:Storage
+ * @description
+ * ComposedStorage stores data to multiple storage mechanisms.
+ * @example Store to LRU and Level
+ * await ComposedStorage(await LRUStorage(), await LevelStorage())
+ * @example Store to memory and IPFS
+ * await ComposedStorage(await MemoryStorage(), await IPFSBlockStorage())
+ * @example Store to LRU and a nested ComposedStorage
+ * const storage1 = await ComposedStorage(await LRUStorage(), await LevelStorage())
+ * await ComposedStorage(storage1, await IPFSBlockStorage())
*/
-// Compose storages:
-// const storage1 = await ComposedStorage(await LRUStorage(), await LevelStorage())
-// const storage2 = await ComposedStorage(storage1, await IPFSBlockStorage())
-
+/**
+ * Creates an instance of ComposedStorage.
+ * @function
+ * @param {module:Storage} storage1 A storage instance.
+ * @param {module:Storage} storage2 A storage instance.
+ * @returns {module:Storage.Storage-Composed} An instance of ComposedStorage.
+ * @memberof module:Storage
+ * @instance
+ */
const ComposedStorage = async (storage1, storage2) => {
+ /**
+ * Puts data to all configured storages.
+ * @function
+ * @param {string} hash The hash of the data to put.
+ * @param {*} data The data to store.
+ * @memberof module:Storage.Storage-Composed
+ * @instance
+ */
const put = async (hash, data) => {
await storage1.put(hash, data)
await storage2.put(hash, data)
}
+ /**
+ * Gets data from the composed storage.
+ *
+ * Get will fetch the data from storage1 first. If no value is found, an
+ * attempt is made to fetch the data from storage2. If data exists in
+ * storage2 but not in storage1, the data is added to storage1.
+ * @function
+ * @param {string} hash The hash of the data to get.
+ * @memberof module:Storage.Storage-Composed
+ * @instance
+ */
const get = async (hash) => {
let value = await storage1.get(hash)
if (!value) {
@@ -24,6 +57,13 @@ const ComposedStorage = async (storage1, storage2) => {
return value
}
+ /**
+ * Iterates over records stored in both storages.
+ * @function
+ * @yields [string, string] The next key/value pair from all storages.
+ * @memberof module:Storage.Storage-Composed
+ * @instance
+ */
const iterator = async function * () {
const keys = []
for (const storage of [storage1, storage2]) {
@@ -36,6 +76,13 @@ const ComposedStorage = async (storage1, storage2) => {
}
}
+ /**
+ * Merges data from another source into each of the composed storages.
+ * @function
+ * @param {module:Storage} other Another storage instance.
+ * @memberof module:Storage.Storage-Composed
+ * @instance
+ */
const merge = async (other) => {
await storage1.merge(other)
await storage2.merge(other)
@@ -43,11 +90,23 @@ const ComposedStorage = async (storage1, storage2) => {
await other.merge(storage2)
}
+ /**
+ * Calls clear on each of the composed storages.
+ * @function
+ * @memberof module:Storage.Storage-Composed
+ * @instance
+ */
const clear = async () => {
await storage1.clear()
await storage2.clear()
}
+ /**
+ * Calls close on each of the composed storages.
+ * @function
+ * @memberof module:Storage.Storage-Composed
+ * @instance
+ */
const close = async () => {
await storage1.close()
await storage2.close()
diff --git a/src/storage/index.js b/src/storage/index.js
index 5d40653..6ceb40e 100644
--- a/src/storage/index.js
+++ b/src/storage/index.js
@@ -1,4 +1,60 @@
-/** @module Storage */
+/**
+ * @module Storage
+ * @description
+ * Various storage mechanisms with a common interface.
+ *
+ * ## Custom Storage
+ * Custom storage modules can be created for special use cases. A storage
+ * module must take the following form:
+ * ```javascript
+ * const CustomStorage = async (params) => { // drop params if not required
+ * const put = async (hash, data) => {
+ * // puts the hash and data to the underlying storage.
+ * }
+ *
+ * const get = async (hash) => {
+ * // gets a record identified by hash from the underlying storage
+ * }
+ *
+ * const del = async (hash) => {
+ * // deletes a record identified by hash from the underlying storage
+ * }
+ *
+ * const iterator = async function * () {
+ * // iterates over the underlying storage's records
+ * // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator
+ * }
+ *
+ * const merge = async (other) => {
+ * // merges the records from two storages
+ * }
+ *
+ * const clear = async () => {
+ * // clears all records from the underlying storage
+ * }
+ *
+ * const close = async () => {
+ * // closes the underlying storage
+ * }
+ *
+ * return {
+ * put,
+ * del,
+ * get,
+ * iterator,
+ * merge,
+ * clear,
+ * close
+ * }
+ * }
+ * ```
+ * All functions must be defined but do not necessarily need to be implemented.
+ * For example, if the storage does not require closing, the close function can
+ * remain empty. For example:
+ * ```JavaScript
+ * const close = async () => {}
+ * ```
+ */
export { default as ComposedStorage } from './composed.js'
export { default as IPFSBlockStorage } from './ipfs-block.js'
export { default as LevelStorage } from './level.js'
diff --git a/src/storage/ipfs-block.js b/src/storage/ipfs-block.js
index 8f44dd3..6801c4e 100644
--- a/src/storage/ipfs-block.js
+++ b/src/storage/ipfs-block.js
@@ -1,17 +1,41 @@
/**
* @namespace Storage-IPFS
* @memberof module:Storage
+ * @description
+ * IPFSBlockStorage uses IPFS to store data as raw blocks.
*/
import { CID } from 'multiformats/cid'
import { base58btc } from 'multiformats/bases/base58'
const defaultTimeout = 30000
+/**
+ * Creates an instance of IPFSBlockStorage.
+ * @function
+ * @param {Object} params One or more parameters for configuring
+ * IPFSBlockStorage.
+ * @param {IPFS} params.ipfs An IPFS instance.
+ * @param {number} [params.timeout=defaultTimeout] A timeout in ms.
+ * @param {boolean} [params.pin=false] True, if the block should be pinned,
+ * false otherwise.
+ * @returns {module:Storage.Storage-IPFS} An instance of IPFSBlockStorage.
+ * @memberof module:Storage
+ * @throw An instance of ipfs is required if params.ipfs is not specified.
+ * @instance
+ */
const IPFSBlockStorage = async ({ ipfs, timeout, pin } = {}) => {
if (!ipfs) throw new Error('An instance of ipfs is required.')
timeout = timeout || defaultTimeout
+ /**
+ * Puts data to an IPFS block.
+ * @function
+ * @param {string} hash The hash of the block to put.
+ * @param {*} data The data to store in the IPFS block.
+ * @memberof module:Storage.Storage-IPFS
+ * @instance
+ */
const put = async (hash, data) => {
const cid = CID.parse(hash, base58btc)
await ipfs.block.put(data, {
@@ -26,6 +50,13 @@ const IPFSBlockStorage = async ({ ipfs, timeout, pin } = {}) => {
const del = async (hash) => {}
+ /**
+ * Gets data from an IPFS block.
+ * @function
+ * @param {string} hash The hash of the block to get.
+ * @memberof module:Storage.Storage-IPFS
+ * @instance
+ */
const get = async (hash) => {
const cid = CID.parse(hash, base58btc)
const block = await ipfs.block.get(cid, { timeout })
diff --git a/src/storage/level.js b/src/storage/level.js
index 1717a60..fcdcef4 100644
--- a/src/storage/level.js
+++ b/src/storage/level.js
@@ -1,26 +1,65 @@
/**
* @namespace Storage-Level
* @memberof module:Storage
+ * @description
+ * LevelStorage stores data to a Level-compatible database.
+ *
+ * To learn more about Level, see {@link https://github.com/Level/level}.
*/
import { Level } from 'level'
+const defaultPath = './level'
const defaultValueEncoding = 'view'
+/**
+ * Creates an instance of LevelStorage.
+ * @function
+ * @param {Object} [params={}] One or more parameters for configuring
+ * LevelStorage.
+ * @param {string} [params.path=defaultPath] The Level path.
+ * @param {string} [params.valueEncoding=defaultValueEncoding] Value encoding.
+ * @returns {module:Storage.Storage-Level} An instance of LevelStorage.
+ * @memberof module:Storage
+ * @instance
+ */
const LevelStorage = async ({ path, valueEncoding } = {}) => {
- path = path || './level'
+ path = path || defaultPath
valueEncoding = valueEncoding || defaultValueEncoding
const db = new Level(path, { valueEncoding, passive: true })
await db.open()
+ /**
+ * Puts data to Level.
+ * @function
+ * @param {string} hash The hash of the data to put.
+ * @param {*} data The data to store.
+ * @memberof module:Storage.Storage-Level
+ * @instance
+ */
const put = async (hash, value) => {
await db.put(hash, value)
}
+ /**
+ * Deletes data from Level.
+ * @function
+ * @param {string} hash The hash of the data to delete.
+ * @param {*} data The data to store.
+ * @memberof module:Storage.Storage-Level
+ * @instance
+ */
const del = async (hash) => {
await db.del(hash)
}
+ /**
+ * Gets data from Level.
+ * @function
+ * @param {string} hash The hash of the data to get.
+ * @memberof module:Storage.Storage-Level
+ * @instance
+ */
const get = async (hash) => {
try {
const value = await db.get(hash)
@@ -32,18 +71,36 @@ const LevelStorage = async ({ path, valueEncoding } = {}) => {
}
}
+ /**
+ * Iterates over records stored in Level.
+ * @function
+ * @yields [string, string] The next key/value pair from Level.
+ * @memberof module:Storage.Storage-Level
+ * @instance
+ */
const iterator = async function * () {
for await (const [key, value] of db.iterator()) {
yield [key, value]
}
}
-
const merge = async (other) => {}
+ /**
+ * Clears the contents of the Level db.
+ * @function
+ * @memberof module:Storage.Storage-Level
+ * @instance
+ */
const clear = async () => {
await db.clear()
}
+ /**
+ * Closes the Level db.
+ * @function
+ * @memberof module:Storage.Storage-Level
+ * @instance
+ */
const close = async () => {
await db.close()
}
diff --git a/src/storage/lru.js b/src/storage/lru.js
index 2998dbd..4384cb6 100644
--- a/src/storage/lru.js
+++ b/src/storage/lru.js
@@ -1,26 +1,67 @@
/**
* @namespace Storage-LRU
* @memberof module:Storage
+ * @description
+ * LRUStorage stores data in a Least Recently Used (LRU) cache.
*/
import LRU from 'lru'
const defaultSize = 1000000
+/**
+ * Creates an instance of LRUStorage.
+ * @function
+ * @param {Object} [params={}] One or more parameters for configuring
+ * IPFSBlockStorage.
+ * @param {string} [params.size=defaultSize] The number of elements to store.
+ * @returns {module:Storage.Storage-LRU} An instance of LRUStorage.
+ * @memberof module:Storage
+ * @instance
+ */
const LRUStorage = async ({ size } = {}) => {
let lru = new LRU(size || defaultSize)
+ /**
+ * Puts data to the LRU cache.
+ * @function
+ * @param {string} hash The hash of the data to put.
+ * @param {*} data The data to store.
+ * @memberof module:Storage.Storage-LRU
+ * @instance
+ */
const put = async (hash, data) => {
lru.set(hash, data)
}
+ /**
+ * Deletes data from the LRU cache.
+ * @function
+ * @param {string} hash The hash of the data to delete.
+ * @memberof module:Storage.Storage-LRU
+ * @instance
+ */
const del = async (hash) => {
lru.remove(hash)
}
+ /**
+ * Gets data from the LRU cache.
+ * @function
+ * @param {string} hash The hash of the data to get.
+ * @memberof module:Storage.Storage-LRU
+ * @instance
+ */
const get = async (hash) => {
return lru.get(hash)
}
+ /**
+ * Iterates over records stored in the LRU cache.
+ * @function
+ * @yields [string, string] The next key/value pair from the LRU cache.
+ * @memberof module:Storage.Storage-LRU
+ * @instance
+ */
const iterator = async function * () {
for await (const key of lru.keys) {
const value = lru.get(key)
@@ -28,6 +69,13 @@ const LRUStorage = async ({ size } = {}) => {
}
}
+ /**
+ * Merges data from another source into the LRU cache.
+ * @function
+ * @param {module:Storage} other Another storage instance.
+ * @memberof module:Storage.Storage-LRU
+ * @instance
+ */
const merge = async (other) => {
if (other) {
for await (const [key, value] of other.iterator()) {
@@ -36,6 +84,12 @@ const LRUStorage = async ({ size } = {}) => {
}
}
+ /**
+ * Clears the contents of the LRU cache.
+ * @function
+ * @memberof module:Storage.Storage-LRU
+ * @instance
+ */
const clear = async () => {
lru = new LRU(size || defaultSize)
}
diff --git a/src/storage/memory.js b/src/storage/memory.js
index 8f6796b..ff96549 100644
--- a/src/storage/memory.js
+++ b/src/storage/memory.js
@@ -1,28 +1,74 @@
/**
* @namespace Storage-Memory
* @memberof module:Storage
+ * @description
+ * MemoryStorage stores data in memory.
*/
+
+/**
+ * Creates an instance of MemoryStorage.
+ * @function
+ * @returns {module:Storage.Storage-Memory} An instance of LRUStorage.
+ * @memberof module:Storage
+ * @instance
+ */
const MemoryStorage = async () => {
let memory = {}
+ /**
+ * Puts data to memory.
+ * @function
+ * @param {string} hash The hash of the data to put.
+ * @param {*} data The data to store.
+ * @memberof module:Storage.Storage-Memory
+ * @instance
+ */
const put = async (hash, data) => {
memory[hash] = data
}
+ /**
+ * Deletes data from memory.
+ * @function
+ * @param {string} hash The hash of the data to delete.
+ * @memberof module:Storage.Storage-Memory
+ * @instance
+ */
const del = async (hash) => {
delete memory[hash]
}
+ /**
+ * Gets data from memory.
+ * @function
+ * @param {string} hash The hash of the data to get.
+ * @memberof module:Storage.Storage-Memory
+ * @instance
+ */
const get = async (hash) => {
return memory[hash]
}
+ /**
+ * Iterates over records stored in memory.
+ * @function
+ * @yields [string, string] The next key/value pair from memory.
+ * @memberof module:Storage.Storage-Memory
+ * @instance
+ */
const iterator = async function * () {
for await (const [key, value] of Object.entries(memory)) {
yield [key, value]
}
}
+ /**
+ * Merges data from another source into memory.
+ * @function
+ * @param {module:Storage} other Another storage instance.
+ * @memberof module:Storage.Storage-Memory
+ * @instance
+ */
const merge = async (other) => {
if (other) {
for await (const [key, value] of other.iterator()) {
@@ -31,6 +77,12 @@ const MemoryStorage = async () => {
}
}
+ /**
+ * Clears the contents of memory.
+ * @function
+ * @memberof module:Storage.Storage-Memory
+ * @instance
+ */
const clear = async () => {
memory = {}
}
diff --git a/src/utils/create-id.js b/src/utils/create-id.js
index 1cbe592..4c8d774 100644
--- a/src/utils/create-id.js
+++ b/src/utils/create-id.js
@@ -1,4 +1,10 @@
-// Source: https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript
+/**
+ * Creates an id from an alphanumeric character list.
+ * @param {number} [length=32] The length of the id.
+ * @returns {string} An id.
+ * @see {@link https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript}
+ * @memberof module:Utils
+ */
const createId = async (length = 32) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
diff --git a/src/utils/ensure-ac-address.js b/src/utils/ensure-ac-address.js
index c32bb89..9ed1660 100644
--- a/src/utils/ensure-ac-address.js
+++ b/src/utils/ensure-ac-address.js
@@ -1,6 +1,12 @@
import pathJoin from './path-join.js'
-// Make sure the given address has '/_access' as the last part
+/**
+ * Checks that the given address has '/_access' as the last part.
+ * @function
+ * @param {string} address The address to check.
+ * @returns {string} The address appended with /_access.
+ * @memberof module:Utils
+ */
export default address => {
const suffix = address.toString().split('/').pop()
return suffix === '_access'
diff --git a/src/utils/index.js b/src/utils/index.js
index a6ac16f..6bf4cb5 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -1,3 +1,8 @@
+/**
+ * Various utility functions.
+ * @module Utils
+ */
+
import createId from './create-id.js'
export {
diff --git a/src/utils/path-join.js b/src/utils/path-join.js
index 880cc21..704b696 100644
--- a/src/utils/path-join.js
+++ b/src/utils/path-join.js
@@ -1,12 +1,35 @@
+/**
+ * A posix-compatible verions of join.
+ * @function posixJoin
+ * @param {...string} paths or more strings to join.
+ * @return {string} The joined strings.
+ * @memberof module:Utils
+ */
export const posixJoin = (...paths) => paths
.join('/')
.replace(/((?<=\/)\/+)|(^\.\/)|((?<=\/)\.\/)/g, '') || '.'
+/**
+ * A windows-compatible verions of join.
+ * @function win32Join
+ * @param {...string} One or more strings to join.
+ * @return {string} The joined strings.
+ * @memberof module:Utils
+ */
export const win32Join = (...paths) => paths
.join('\\')
.replace(/\//g, '\\')
.replace(/((?<=\\)\\+)|(^\.\\)|((?<=\\)\.\\)/g, '') || '.'
+/**
+ * An alias for posixJoin.
+ * @function join
+ * @alias posixJoin
+ * @param {...string} paths or more strings to join.
+ * @return {string} The joined strings.
+ * @memberof module:Utils
+ * @static
+ */
export const join = posixJoin
export default posixJoin
diff --git a/test/orbitdb.test.js b/test/orbitdb.test.js
index 9c61149..9f856d2 100644
--- a/test/orbitdb.test.js
+++ b/test/orbitdb.test.js
@@ -231,7 +231,7 @@ describe('OrbitDB', function () {
err = e
}
notStrictEqual(err, undefined)
- strictEqual(err.message, 'IPFS instance is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
+ strictEqual(err.message, 'IPFS instance is a required argument.')
})
it('throws an error if IPFS instance is not given', async () => {
@@ -242,7 +242,7 @@ describe('OrbitDB', function () {
err = e
}
notStrictEqual(err, undefined)
- strictEqual(err.message, 'IPFS instance is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
+ strictEqual(err.message, 'IPFS instance is a required argument.')
})
it('doesn\'t create the data directory when an error occurs', async () => {