/** * @module OrbitDB * @description Provides an interface for users to interact with OrbitDB. */ import { getDatabaseType } from './databases/index.js' import KeyStore from './key-store.js' import { Identities } from './identities/index.js' import OrbitDBAddress, { isValidAddress } from './address.js' import ManifestStore from './manifest-store.js' import { createId } from './utils/index.js' import pathJoin from './utils/path-join.js' import { getAccessController } from './access-controllers/index.js' import IPFSAccessController from './access-controllers/ipfs.js' 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 {module:Identities} [params.identities] An Identities 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, identities, 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.') } id = id || await createId() const { id: peerId } = await ipfs.id() directory = directory || './orbitdb' let keystore if (identities) { keystore = identities.keystore } else { keystore = await KeyStore({ path: pathJoin(directory, './keystore') }) identities = await Identities({ ipfs, keystore }) } const identity = await identities.createIdentity({ id }) const manifestStore = await ManifestStore({ ipfs }) 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. * To open an existing database, pass its address to the `open` function: * ``` * const existingDB = await orbitdb.open(dbAddress) * ``` * The address of a newly created database can be retrieved using * `db.address`. * @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. Only applies when * creating a database and is not used when opening an existing database. * @param {boolean} [params.sync=true] 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]{@link module:AccessControllers.AccessControllers-IPFS}] * 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, LevelStorage). * @param {module:Storage} [params.entryStorage=[ComposedStorage]{@link module:Storage.Storage-Composed}] A compatible storage instance for storing * log entries. Defaults to ComposedStorage(LRUStorage, IPFSBlockStorage). * @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 number of references to * previous entries that should be stored with this entry. * @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 if (databases[address]) { return databases[address] } if (isValidAddress(address)) { // If the address given was a valid OrbitDB address, eg. '/orbitdb/zdpuAuK3BHpS7NvMBivynypqciYCuy2UW77XYBPUYRnLjnw13' const addr = OrbitDBAddress(address) manifest = await manifestStore.get(addr.hash) const acType = manifest.accessController.split('/', 2).pop() const acAddress = manifest.accessController.replaceAll(`/${acType}/`, '') AccessController = getAccessController(acType)() accessController = await AccessController({ orbitdb: { open, identity, ipfs }, identities, address: acAddress }) name = manifest.name type = type || manifest.type meta = manifest.meta } else { // If the address given was not valid, eg. just the name of the database type = type || DefaultDatabaseType AccessController = AccessController || DefaultAccessController() accessController = await AccessController({ orbitdb: { open, identity, ipfs }, identities }) const m = await manifestStore.create({ name: address, type, accessController: accessController.address, meta }) manifest = m.manifest address = OrbitDBAddress(m.hash) name = manifest.name meta = manifest.meta } Database = Database || getDatabaseType(type)() if (!Database) { throw new Error(`Unsupported database type: '${type}'`) } address = address.toString() const db = await Database({ ipfs, identity, address, name, access: accessController, directory, meta, syncAutomatically: sync, headsStorage, entryStorage, indexStorage, referencesCount }) db.events.on('close', onDatabaseClosed(address)) databases[address] = db return db } const onDatabaseClosed = (address) => () => { 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() } if (manifestStore) { await manifestStore.close() } databases = {} } return { open, stop, ipfs, directory, keystore, identity, peerId } } export { OrbitDB as default, OrbitDBAddress }