From 084b213dc6bc19d813867eecdef2af718c8738f7 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 30 Oct 2020 23:52:15 -0700 Subject: [PATCH 01/29] chore: make IPFS api static --- packages/ipfs-cli/src/commands/init.js | 28 +- packages/ipfs-core/src/api-manager.js | 43 -- .../ipfs-core/src/components/add-all/index.js | 9 +- .../ipfs-core/src/components/bitswap/index.js | 27 + .../ipfs-core/src/components/bitswap/stat.js | 16 +- .../src/components/bitswap/unwant.js | 18 +- .../components/bitswap/wantlist-for-peer.js | 22 +- .../src/components/bitswap/wantlist.js | 16 +- .../ipfs-core/src/components/block/get.js | 16 +- .../ipfs-core/src/components/block/index.js | 36 + .../ipfs-core/src/components/block/put.js | 17 +- packages/ipfs-core/src/components/block/rm.js | 11 +- .../ipfs-core/src/components/block/stat.js | 10 +- .../ipfs-core/src/components/bootstrap/add.js | 13 +- .../src/components/bootstrap/clear.js | 8 +- .../src/components/bootstrap/index.js | 28 + .../src/components/bootstrap/list.js | 11 +- .../src/components/bootstrap/reset.js | 9 +- .../ipfs-core/src/components/bootstrap/rm.js | 10 +- packages/ipfs-core/src/components/config.js | 74 +- packages/ipfs-core/src/components/core.js | 45 ++ packages/ipfs-core/src/components/dag/get.js | 39 +- .../ipfs-core/src/components/dag/index.js | 69 ++ packages/ipfs-core/src/components/dag/put.js | 130 ++-- .../ipfs-core/src/components/dag/resolve.js | 7 +- packages/ipfs-core/src/components/dag/tree.js | 10 +- packages/ipfs-core/src/components/dht.js | 35 +- .../ipfs-core/src/components/files/index.js | 8 +- packages/ipfs-core/src/components/files/ls.js | 16 +- packages/ipfs-core/src/components/gc-lock.js | 24 + packages/ipfs-core/src/components/id.js | 15 +- packages/ipfs-core/src/components/index.js | 653 ++++++++++-------- packages/ipfs-core/src/components/init.js | 561 --------------- packages/ipfs-core/src/components/ipld.js | 23 + packages/ipfs-core/src/components/ipns.js | 110 +++ .../ipfs-core/src/components/is-online.js | 12 +- .../ipfs-core/src/components/key/export.js | 29 +- packages/ipfs-core/src/components/key/gen.js | 43 +- .../ipfs-core/src/components/key/import.js | 33 +- .../ipfs-core/src/components/key/index.js | 31 + packages/ipfs-core/src/components/key/info.js | 20 +- packages/ipfs-core/src/components/key/list.js | 37 +- .../ipfs-core/src/components/key/rename.js | 38 +- packages/ipfs-core/src/components/key/rm.js | 32 +- packages/ipfs-core/src/components/libp2p.js | 21 +- packages/ipfs-core/src/components/ls.js | 4 +- .../ipfs-core/src/components/name/index.js | 41 ++ .../ipfs-core/src/components/name/publish.js | 16 +- .../src/components/name/pubsub/cancel.js | 14 +- .../src/components/name/pubsub/index.js | 25 + .../src/components/name/pubsub/state.js | 16 +- .../src/components/name/pubsub/subs.js | 15 +- .../src/components/name/pubsub/utils.js | 14 +- .../ipfs-core/src/components/name/resolve.js | 28 +- .../ipfs-core/src/components/name/utils.js | 20 +- packages/ipfs-core/src/components/network.js | 129 ++++ .../ipfs-core/src/components/object/data.js | 23 +- .../ipfs-core/src/components/object/get.js | 30 +- .../ipfs-core/src/components/object/index.js | 39 ++ .../ipfs-core/src/components/object/links.js | 20 +- .../ipfs-core/src/components/object/new.js | 28 +- .../src/components/object/patch/add-link.js | 12 +- .../components/object/patch/append-data.js | 13 +- .../src/components/object/patch/index.js | 30 + .../src/components/object/patch/rm-link.js | 12 +- .../src/components/object/patch/set-data.js | 12 +- .../ipfs-core/src/components/object/put.js | 27 +- .../ipfs-core/src/components/object/stat.js | 32 +- .../ipfs-core/src/components/pin/add-all.js | 17 +- .../ipfs-core/src/components/pin/index.js | 35 + packages/ipfs-core/src/components/pin/ls.js | 12 +- .../src/components/pin/pin-manager.js | 74 +- .../ipfs-core/src/components/pin/rm-all.js | 18 +- packages/ipfs-core/src/components/ping.js | 7 +- packages/ipfs-core/src/components/pubsub.js | 153 +++- .../ipfs-core/src/components/refs/index.js | 35 +- .../ipfs-core/src/components/refs/local.js | 15 +- packages/ipfs-core/src/components/repo/gc.js | 9 +- .../ipfs-core/src/components/repo/index.js | 29 + .../ipfs-core/src/components/repo/stat.js | 13 +- .../ipfs-core/src/components/repo/version.js | 14 +- packages/ipfs-core/src/components/resolve.js | 6 +- packages/ipfs-core/src/components/start.js | 414 +---------- packages/ipfs-core/src/components/stats/bw.js | 53 +- .../ipfs-core/src/components/stats/index.js | 29 + packages/ipfs-core/src/components/stop.js | 247 +------ packages/ipfs-core/src/components/storage.js | 295 ++++++++ .../ipfs-core/src/components/swarm/addrs.js | 27 +- .../ipfs-core/src/components/swarm/connect.js | 20 +- .../src/components/swarm/disconnect.js | 20 +- .../ipfs-core/src/components/swarm/index.js | 29 + .../src/components/swarm/local-addrs.js | 25 +- .../ipfs-core/src/components/swarm/peers.js | 39 +- packages/ipfs-core/src/components/version.js | 8 +- packages/ipfs-core/src/errors.js | 22 + packages/ipfs-core/src/index.js | 166 +---- packages/ipfs-core/src/interface/basic.ts | 28 + packages/ipfs-core/src/interface/bitswap.ts | 88 +++ .../ipfs-core/src/interface/block-service.ts | 20 + packages/ipfs-core/src/interface/datastore.ts | 185 +++++ packages/ipfs-core/src/interface/format.ts | 41 ++ packages/ipfs-core/src/interface/ipld.ts | 43 ++ .../ipfs-core/src/interface/moving-avarage.ts | 9 + packages/ipfs-core/src/interface/repo.ts | 104 +++ packages/ipfs-core/src/interface/store.ts | 124 ++++ packages/ipfs-core/src/mfs-preload.js | 6 +- packages/ipfs-core/src/preload.js | 32 +- packages/ipfs-core/src/runtime/repo-nodejs.js | 6 + packages/ipfs-core/src/utils.js | 23 +- packages/ipfs-core/src/utils/service.js | 239 +++++++ packages/ipfs-http-client/src/block/put.js | 2 + packages/ipfs-http-client/src/dag/put.js | 7 +- 112 files changed, 3727 insertions(+), 2124 deletions(-) delete mode 100644 packages/ipfs-core/src/api-manager.js create mode 100644 packages/ipfs-core/src/components/bitswap/index.js create mode 100644 packages/ipfs-core/src/components/block/index.js create mode 100644 packages/ipfs-core/src/components/bootstrap/index.js create mode 100644 packages/ipfs-core/src/components/core.js create mode 100644 packages/ipfs-core/src/components/dag/index.js create mode 100644 packages/ipfs-core/src/components/gc-lock.js delete mode 100644 packages/ipfs-core/src/components/init.js create mode 100644 packages/ipfs-core/src/components/ipld.js create mode 100644 packages/ipfs-core/src/components/ipns.js create mode 100644 packages/ipfs-core/src/components/key/index.js create mode 100644 packages/ipfs-core/src/components/name/index.js create mode 100644 packages/ipfs-core/src/components/name/pubsub/index.js create mode 100644 packages/ipfs-core/src/components/network.js create mode 100644 packages/ipfs-core/src/components/object/index.js create mode 100644 packages/ipfs-core/src/components/object/patch/index.js create mode 100644 packages/ipfs-core/src/components/pin/index.js create mode 100644 packages/ipfs-core/src/components/repo/index.js create mode 100644 packages/ipfs-core/src/components/stats/index.js create mode 100644 packages/ipfs-core/src/components/storage.js create mode 100644 packages/ipfs-core/src/components/swarm/index.js create mode 100644 packages/ipfs-core/src/interface/basic.ts create mode 100644 packages/ipfs-core/src/interface/bitswap.ts create mode 100644 packages/ipfs-core/src/interface/block-service.ts create mode 100644 packages/ipfs-core/src/interface/datastore.ts create mode 100644 packages/ipfs-core/src/interface/format.ts create mode 100644 packages/ipfs-core/src/interface/ipld.ts create mode 100644 packages/ipfs-core/src/interface/moving-avarage.ts create mode 100644 packages/ipfs-core/src/interface/repo.ts create mode 100644 packages/ipfs-core/src/interface/store.ts create mode 100644 packages/ipfs-core/src/utils/service.js diff --git a/packages/ipfs-cli/src/commands/init.js b/packages/ipfs-cli/src/commands/init.js index 2a5f9fd0c1..a324d87c9f 100644 --- a/packages/ipfs-cli/src/commands/init.js +++ b/packages/ipfs-cli/src/commands/init.js @@ -69,22 +69,20 @@ module.exports = { const IPFS = require('ipfs-core') const Repo = require('ipfs-repo') - const node = await IPFS.create({ - repo: new Repo(repoPath), - init: false, - start: false, - config - }) - try { - await node.init({ - algorithm: argv.algorithm, - bits: argv.bits, - privateKey: argv.privateKey, - emptyRepo: argv.emptyRepo, - profiles: argv.profile, - pass: argv.pass, - log: print + await IPFS.create({ + repo: new Repo(repoPath), + init: { + algorithm: argv.algorithm, + bits: argv.bits, + privateKey: argv.privateKey, + emptyRepo: argv.emptyRepo, + profiles: argv.profile, + pass: argv.pass + }, + start: false, + // @ts-ignore - Expects more than {} + config }) } catch (err) { if (err.code === 'EACCES') { diff --git a/packages/ipfs-core/src/api-manager.js b/packages/ipfs-core/src/api-manager.js deleted file mode 100644 index 03a33b925d..0000000000 --- a/packages/ipfs-core/src/api-manager.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict' - -module.exports = class ApiManager { - /** - * @callback UndefFn - * @param {PropertyKey} prop - */ - - /** - * @template API - * @typedef {{ cancel: () => any, api: API }} Updated - */ - - constructor () { - this._api = {} - /** - * @type {UndefFn} - * @returns {any} - */ - this._onUndef = () => undefined - this.api = new Proxy(this._api, { - get: (_, prop) => { - if (prop === 'then') return undefined // Not a promise! - return this._api[prop] === undefined ? this._onUndef(prop) : this._api[prop] - } - }) - } - - /** - * @template A - * @param {A} nextApi - * @param {UndefFn} [onUndef] - * @returns {Updated} - */ - update (nextApi, onUndef) { - const prevApi = { ...this._api } - const prevUndef = this._onUndef - Object.keys(this._api).forEach(k => { delete this._api[k] }) - const api = Object.assign(this._api, nextApi) - if (onUndef) this._onUndef = onUndef - return { cancel: () => this.update(prevApi, prevUndef), api } - } -} diff --git a/packages/ipfs-core/src/components/add-all/index.js b/packages/ipfs-core/src/components/add-all/index.js index c5c7502ddb..8b0f429428 100644 --- a/packages/ipfs-core/src/components/add-all/index.js +++ b/packages/ipfs-core/src/components/add-all/index.js @@ -13,10 +13,10 @@ const mergeOptions = require('merge-options').bind({ ignoreUndefined: true }) * @param {import('..').GCLock} config.gcLock * @param {import('..').Preload} config.preload * @param {import('..').Pin} config.pin - * @param {import('../init').ConstructorOptions} config.options + * @param {ShardingOptions} [config.options] */ -module.exports = ({ block, gcLock, preload, pin, options: constructorOptions }) => { - const isShardingEnabled = constructorOptions.EXPERIMENTAL && constructorOptions.EXPERIMENTAL.sharding +module.exports = ({ block, gcLock, preload, pin, options }) => { + const isShardingEnabled = options && options.sharding /** * Import multiple files and data into IPFS. * @@ -178,4 +178,7 @@ function pinFile (pin, opts) { * @typedef {import('../../utils').MTime} MTime * @typedef {import('../../utils').AbortOptions} AbortOptions * @typedef {import('..').CID} CID + * + * @typedef {Object} ShardingOptions + * @property {boolean} [sharding] */ diff --git a/packages/ipfs-core/src/components/bitswap/index.js b/packages/ipfs-core/src/components/bitswap/index.js new file mode 100644 index 0000000000..8209007a75 --- /dev/null +++ b/packages/ipfs-core/src/components/bitswap/index.js @@ -0,0 +1,27 @@ +'use strict' + +const createWantlist = require('./wantlist') +const createWantlistForPeer = require('./wantlist-for-peer') +const createUnwant = require('./unwant') +const createStat = require('./stat') + +class BitswapAPI { + /** + * @param {Object} config + * @param {NetworkService} config.network + */ + constructor ({ network }) { + this.wantlist = createWantlist({ network }) + this.wantlistForPeer = createWantlistForPeer({ network }) + this.unwant = createUnwant({ network }) + this.stat = createStat({ network }) + } +} +module.exports = BitswapAPI + +/** + * @typedef {import('..').NetworkService} NetworkService + * @typedef {import('..').PeerId} PeerId + * @typedef {import('..').CID} CID + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/bitswap/stat.js b/packages/ipfs-core/src/components/bitswap/stat.js index 237a54d80a..63ce7f5551 100644 --- a/packages/ipfs-core/src/components/bitswap/stat.js +++ b/packages/ipfs-core/src/components/bitswap/stat.js @@ -6,16 +6,13 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBitSwap} config.bitswap + * @param {import('.').NetworkService} config.network */ -module.exports = ({ bitswap }) => { +module.exports = ({ network }) => { /** * Show diagnostic information on the bitswap agent. * Note: `bitswap.stat` and `stats.bitswap` can be used interchangeably. * - * @param {import('../../utils').AbortOptions} [_options] - * @returns {Promise} - * * @example * ```js * const stats = await ipfs.bitswap.stat() @@ -35,8 +32,11 @@ module.exports = ({ bitswap }) => { * // dupDataReceived: 0 * // } * ``` + * @param {import('.').AbortOptions} [options] + * @returns {Promise} */ - async function stat (_options) { // eslint-disable-line require-await + async function stat (options) { + const { bitswap } = await network.use(options) const snapshot = bitswap.stat().snapshot return { @@ -59,13 +59,11 @@ module.exports = ({ bitswap }) => { * @typedef {object} BitswapStats - An object that contains information about the bitswap agent * @property {number} provideBufLen - an integer * @property {CID[]} wantlist - * @property {string[]} peers - array of peer IDs as Strings + * @property {CID[]} peers - array of peer IDs as Strings * @property {Big} blocksReceived * @property {Big} dataReceived * @property {Big} blocksSent * @property {Big} dataSent * @property {Big} dupBlksReceived * @property {Big} dupDataReceived - * - * @typedef {import('..').CID} CID */ diff --git a/packages/ipfs-core/src/components/bitswap/unwant.js b/packages/ipfs-core/src/components/bitswap/unwant.js index 455696b23c..a17cb3b355 100644 --- a/packages/ipfs-core/src/components/bitswap/unwant.js +++ b/packages/ipfs-core/src/components/bitswap/unwant.js @@ -6,15 +6,12 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBitSwap} config.bitswap + * @param {import('.').NetworkService} config.network */ -module.exports = ({ bitswap }) => { +module.exports = ({ network }) => { /** * Removes one or more CIDs from the wantlist * - * @param {CID | CID[]} cids - The CIDs to remove from the wantlist - * @param {AbortOptions} [options] - * @returns {Promise} - A promise that resolves once the request is complete * @example * ```JavaScript * let list = await ipfs.bitswap.wantlist() @@ -27,8 +24,14 @@ module.exports = ({ bitswap }) => { * console.log(list) * // [] * ``` + * + * @param {CID | CID[]} cids - The CIDs to remove from the wantlist + * @param {AbortOptions} [options] + * @returns {Promise} - A promise that resolves once the request is complete */ - async function unwant (cids, options) { // eslint-disable-line require-await + async function unwant (cids, options) { + const { bitswap } = await network.use(options) + if (!Array.isArray(cids)) { cids = [cids] } @@ -46,6 +49,5 @@ module.exports = ({ bitswap }) => { } /** - * @typedef {import('..').CID} CID - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/bitswap/wantlist-for-peer.js b/packages/ipfs-core/src/components/bitswap/wantlist-for-peer.js index 6f79ab8c9d..e9778c6148 100644 --- a/packages/ipfs-core/src/components/bitswap/wantlist-for-peer.js +++ b/packages/ipfs-core/src/components/bitswap/wantlist-for-peer.js @@ -5,24 +5,26 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBitSwap} config.bitswap + * @param {import('.').NetworkService} config.network */ -module.exports = ({ bitswap }) => { +module.exports = ({ network }) => { /** * Returns the wantlist for a connected peer * - * @param {PeerId | CID | string | Uint8Array} peerId - A peer ID to return the wantlist for\ - * @param {AbortOptions} [options] - * @returns {Promise} - An array of CIDs currently in the wantlist - * * @example * ```js * const list = await ipfs.bitswap.wantlistForPeer(peerId) * console.log(list) * // [ CID('QmHash') ] * ``` + * + * @param {PeerId | CID | string | Uint8Array} peerId - A peer ID to return the wantlist for\ + * @param {AbortOptions} [options] + * @returns {Promise} - An array of CIDs currently in the wantlist + * */ - async function wantlistForPeer (peerId, options = {}) { // eslint-disable-line require-await + async function wantlistForPeer (peerId, options = {}) { + const { bitswap } = await network.use(options) const list = bitswap.wantlistForPeer(PeerId.createFromCID(peerId), options) return Array.from(list).map(e => e[1].cid) @@ -32,9 +34,9 @@ module.exports = ({ bitswap }) => { } /** - * @typedef {import('../../utils').AbortOptions} AbortOptions - * @typedef {import('..').CID} CID - * @typedef {import('..').PeerId} PeerId + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').PeerId} PeerId */ /** diff --git a/packages/ipfs-core/src/components/bitswap/wantlist.js b/packages/ipfs-core/src/components/bitswap/wantlist.js index 94fed01ebb..c25195c9a5 100644 --- a/packages/ipfs-core/src/components/bitswap/wantlist.js +++ b/packages/ipfs-core/src/components/bitswap/wantlist.js @@ -4,22 +4,24 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBitSwap} config.bitswap + * @param {import('.').NetworkService} config.network */ -module.exports = ({ bitswap }) => { +module.exports = ({ network }) => { /** * Returns the wantlist for your node * - * @param {AbortOptions} [options] - * @returns {Promise} - An array of CIDs currently in the wantlist. * @example * ```js * const list = await ipfs.bitswap.wantlist() * console.log(list) * // [ CID('QmHash') ] * ``` + * + * @param {AbortOptions} [options] + * @returns {Promise} - An array of CIDs currently in the wantlist. */ - async function wantlist (options = {}) { // eslint-disable-line require-await + async function wantlist (options = {}) { + const { bitswap } = await network.use(options) const list = bitswap.getWantlist(options) return Array.from(list).map(e => e[1].cid) @@ -29,6 +31,6 @@ module.exports = ({ bitswap }) => { } /** - * @typedef {import('../../utils').AbortOptions} AbortOptions - * @typedef {import('..').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID */ diff --git a/packages/ipfs-core/src/components/block/get.js b/packages/ipfs-core/src/components/block/get.js index 4f376fb4a5..225ab16e1b 100644 --- a/packages/ipfs-core/src/components/block/get.js +++ b/packages/ipfs-core/src/components/block/get.js @@ -5,10 +5,10 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBlockService} config.blockService - * @param {import('..').Preload} config.preload + * @param {import('.').BlockService} config.blockService + * @param {import('.').Preload} config.preload */ -module.exports = ({ blockService, preload }) => { +module.exports = ({ preload, blockService }) => { /** * Get a raw IPFS block. * @@ -22,14 +22,14 @@ module.exports = ({ blockService, preload }) => { * console.log(block.data) * ``` */ - async function get (cid, options = {}) { // eslint-disable-line require-await + async function get (cid, options = {}) { cid = cleanCid(cid) if (options.preload !== false) { preload(cid) } - return blockService.get(cid, options) + return await blockService.get(cid, options) } return withTimeoutOption(get) @@ -39,7 +39,7 @@ module.exports = ({ blockService, preload }) => { * @typedef {Object} GetOptions * @property {boolean} [preload=true] * - * @typedef {import('../../utils').AbortOptions} AbortOptions - * @typedef {import('..').CID} CID - * @typedef {import('..').IPLDBlock} IPLDBlock + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').IPLDBlock} IPLDBlock */ diff --git a/packages/ipfs-core/src/components/block/index.js b/packages/ipfs-core/src/components/block/index.js new file mode 100644 index 0000000000..252a49d115 --- /dev/null +++ b/packages/ipfs-core/src/components/block/index.js @@ -0,0 +1,36 @@ +'use strict' + +const createGet = require('./get') +const createPut = require('./put') +const createRm = require('./rm') +const createStat = require('./stat') + +class BlockAPI { + /** + * @param {Object} config + * @param {Preload} config.preload + * @param {BlockService} config.blockService + * @param {GCLock} config.gcLock + * @param {Pin} config.pin + * @param {PinManager} config.pinManager + */ + constructor ({ blockService, preload, gcLock, pinManager, pin }) { + this.get = createGet({ blockService, preload }) + this.put = createPut({ blockService, preload, gcLock, pin }) + this.rm = createRm({ blockService, gcLock, pinManager }) + this.stat = createStat({ blockService, preload }) + } +} + +module.exports = BlockAPI + +/** + * @typedef {import('..').Preload} Preload + * @typedef {import('..').BlockService} BlockService + * @typedef {import('..').GCLock} GCLock + * @typedef {import('..').Pin} Pin + * @typedef {import('..').PinManager} PinManager + * @typedef {import('..').AbortOptions} AbortOptions + * @typedef {import('..').CID} CID + * @typedef {import('..').IPLDBlock} IPLDBlock + */ diff --git a/packages/ipfs-core/src/components/block/put.js b/packages/ipfs-core/src/components/block/put.js index 8303315b59..db395eb7ac 100644 --- a/packages/ipfs-core/src/components/block/put.js +++ b/packages/ipfs-core/src/components/block/put.js @@ -8,12 +8,12 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBlockService} config.blockService - * @param {import('..').Pin} config.pin - * @param {import('..').GCLock} config.gcLock - * @param {import('..').Preload} config.preload + * @param {import('.').BlockService} config.blockService + * @param {import('.').Pin} config.pin + * @param {import('.').GCLock} config.gcLock + * @param {import('.').Preload} config.preload */ -module.exports = ({ blockService, pin, gcLock, preload }) => { +module.exports = ({ blockService, preload, gcLock, pin }) => { /** * Stores input as an IPFS block. * @@ -21,7 +21,7 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { * don't need to pass options, as the block instance will carry the CID * value as a property. * - * @param {Uint8Array | IPLDBlock} block - The block or data to store + * @param {IPLDBlock} block - The block or data to store * @param {PutOptions & AbortOptions} [options] - **Note:** If you pass a `Block` instance as the block parameter, you don't need to pass options, as the block instance will carry the CID value as a property. * @returns {Promise} - A Block type object, containing both the data and the hash of the block * @example @@ -122,8 +122,7 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { * @property {boolean} [pin=false] - If true, pin added blocks recursively (default: `false`) * @property {boolean} [preload] * - * @typedef {import('../../utils').AbortOptions} AbortOptions - * @typedef {import('..').CID} CID - * @typedef {import('..').IPLDBlock} IPLDBlock + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').IPLDBlock} IPLDBlock * @typedef {0|1} CIDVersion */ diff --git a/packages/ipfs-core/src/components/block/rm.js b/packages/ipfs-core/src/components/block/rm.js index db7414e31d..bbfe56f8f0 100644 --- a/packages/ipfs-core/src/components/block/rm.js +++ b/packages/ipfs-core/src/components/block/rm.js @@ -12,11 +12,11 @@ const BLOCK_RM_CONCURRENCY = 8 /** * @param {Object} config - * @param {import('..').IPFSBlockService} config.blockService - * @param {import('../pin/pin-manager')} config.pinManager - * @param {import('..').GCLock} config.gcLock + * @param {import('.').BlockService} config.blockService + * @param {import('.').PinManager} config.pinManager + * @param {import('.').GCLock} config.gcLock */ -module.exports = ({ blockService, gcLock, pinManager }) => { +module.exports = ({ gcLock, blockService, pinManager }) => { /** /** * Remove one or more IPFS block(s). @@ -65,6 +65,7 @@ module.exports = ({ blockService, gcLock, pinManager }) => { } // remove has check when https://github.com/ipfs/js-ipfs-block-service/pull/88 is merged + // @ts-ignore - this accesses some internals const has = await blockService._repo.blocks.has(cid) if (!has) { @@ -96,7 +97,7 @@ module.exports = ({ blockService, gcLock, pinManager }) => { * @property {boolean} [force=false] - Ignores nonexistent blocks * @property {boolean} [quiet=false] - Write minimal output * - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions * * @typedef {RmSucceess|RmFailure} RmResult * Note: If an error is present for a given object, the block with diff --git a/packages/ipfs-core/src/components/block/stat.js b/packages/ipfs-core/src/components/block/stat.js index 62f6c7ea3b..897a3b9263 100644 --- a/packages/ipfs-core/src/components/block/stat.js +++ b/packages/ipfs-core/src/components/block/stat.js @@ -5,9 +5,10 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPFSBlockService} config.blockService - * @param {import('..').Preload} config.preload + * @param {import('.').BlockService} config.blockService + * @param {import('.').Preload} config.preload */ + module.exports = ({ blockService, preload }) => { /** /** @@ -50,7 +51,6 @@ module.exports = ({ blockService, preload }) => { * @typedef {Object} StatOptions * @property {boolean} [preload] * - * @typedef {import('../../utils').AbortOptions} AbortOptions - * - * @typedef {import('..').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID */ diff --git a/packages/ipfs-core/src/components/bootstrap/add.js b/packages/ipfs-core/src/components/bootstrap/add.js index a9f22c43a1..d101c6e7fb 100644 --- a/packages/ipfs-core/src/components/bootstrap/add.js +++ b/packages/ipfs-core/src/components/bootstrap/add.js @@ -4,7 +4,8 @@ const { isValidMultiaddr } = require('./utils') const { withTimeoutOption } = require('../../utils') /** - * @param {import('..').IPFSRepo} repo + * @param {Object} config + * @param {import('.').Repo} config.repo */ module.exports = ({ repo }) => { /** @@ -30,11 +31,13 @@ module.exports = ({ repo }) => { const config = await repo.config.getAll(options) + // @ts-ignore - May not have `Bootstrap` if (config.Bootstrap.indexOf(multiaddr.toString()) === -1) { + // @ts-ignore - May not have `Bootstrap` config.Bootstrap.push(multiaddr.toString()) } - await repo.config.set(config) + await repo.config.replace(config) return { Peers: [multiaddr] @@ -46,7 +49,7 @@ module.exports = ({ repo }) => { /** * @typedef {import('./utils').Peers} Peers - * @typedef {import('../../utils').AbortOptions} AbortOptions - * @typedef {import('..').CID} CID - * @typedef {import('..').Multiaddr} Multiaddr + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').Multiaddr} Multiaddr */ diff --git a/packages/ipfs-core/src/components/bootstrap/clear.js b/packages/ipfs-core/src/components/bootstrap/clear.js index 2637e4d345..9828372106 100644 --- a/packages/ipfs-core/src/components/bootstrap/clear.js +++ b/packages/ipfs-core/src/components/bootstrap/clear.js @@ -5,7 +5,7 @@ const Multiaddr = require('multiaddr') /** * @param {Object} config - * @param {import('..').IPFSRepo} config.repo + * @param {import('.').Repo} config.repo */ module.exports = ({ repo }) => { /** @@ -26,7 +26,7 @@ module.exports = ({ repo }) => { const removed = config.Bootstrap || [] config.Bootstrap = [] - await repo.config.set(config) + await repo.config.replace(config) return { Peers: removed.map(ma => new Multiaddr(ma)) } } @@ -35,8 +35,6 @@ module.exports = ({ repo }) => { } /** - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions * @typedef {import('./utils').Peers} Peers - * @typedef {import('..').CID} CID - * @typedef {import('..').Multiaddr} Multiaddr */ diff --git a/packages/ipfs-core/src/components/bootstrap/index.js b/packages/ipfs-core/src/components/bootstrap/index.js new file mode 100644 index 0000000000..3cb7c9dafa --- /dev/null +++ b/packages/ipfs-core/src/components/bootstrap/index.js @@ -0,0 +1,28 @@ +'use strict' + +const createAdd = require('./add') +const createClear = require('./clear') +const createList = require('./list') +const createReset = require('./reset') +const createRm = require('./rm') +class BootstrapAPI { + /** + * @param {Object} config + * @param {Repo} config.repo + */ + constructor ({ repo }) { + this.add = createAdd({ repo }) + this.list = createList({ repo }) + this.rm = createRm({ repo }) + this.clear = createClear({ repo }) + this.reset = createReset({ repo }) + } +} +module.exports = BootstrapAPI + +/** + * @typedef {import('..').Repo} Repo + * @typedef {import('..').AbortOptions} AbortOptions + * @typedef {import('..').CID} CID + * @typedef {import('..').Multiaddr} Multiaddr + */ diff --git a/packages/ipfs-core/src/components/bootstrap/list.js b/packages/ipfs-core/src/components/bootstrap/list.js index 421f16792f..cb39d5b398 100644 --- a/packages/ipfs-core/src/components/bootstrap/list.js +++ b/packages/ipfs-core/src/components/bootstrap/list.js @@ -4,7 +4,8 @@ const { withTimeoutOption } = require('../../utils') const Multiaddr = require('multiaddr') /** - * @param {import('..').IPFSRepo} repo + * @param {Object} config + * @param {import('.').Repo} config.repo */ module.exports = ({ repo }) => { /** @@ -21,7 +22,8 @@ module.exports = ({ repo }) => { * ``` */ async function list (options) { - const peers = await repo.config.get('Bootstrap', options) + /** @type {string[]|null} */ + const peers = (await repo.config.get('Bootstrap', options)) return { Peers: (peers || []).map(ma => new Multiaddr(ma)) } } @@ -29,8 +31,7 @@ module.exports = ({ repo }) => { } /** - * @typedef {import('../../utils').AbortOptions} AbortOptions * @typedef {import('./utils').Peers} Peers - * @typedef {import('..').CID} CID - * @typedef {import('..').Multiaddr} Multiaddr + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID */ diff --git a/packages/ipfs-core/src/components/bootstrap/reset.js b/packages/ipfs-core/src/components/bootstrap/reset.js index 81a2dfc6bf..7593ae4004 100644 --- a/packages/ipfs-core/src/components/bootstrap/reset.js +++ b/packages/ipfs-core/src/components/bootstrap/reset.js @@ -5,7 +5,8 @@ const { withTimeoutOption } = require('../../utils') const Multiaddr = require('multiaddr') /** - * @param {import('..').IPFSRepo} repo + * @param {Object} config + * @param {import('.').Repo} config.repo */ module.exports = ({ repo }) => { /** @@ -25,7 +26,7 @@ module.exports = ({ repo }) => { const config = await repo.config.getAll(options) config.Bootstrap = defaultConfig().Bootstrap - await repo.config.set(config) + await repo.config.replace(config) return { Peers: defaultConfig().Bootstrap.map(ma => new Multiaddr(ma)) @@ -36,8 +37,6 @@ module.exports = ({ repo }) => { } /** - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions * @typedef {import('./utils').Peers} Peers - * @typedef {import('..').CID} CID - * @typedef {import('..').Multiaddr} Multiaddr */ diff --git a/packages/ipfs-core/src/components/bootstrap/rm.js b/packages/ipfs-core/src/components/bootstrap/rm.js index 88300ea939..bca7eee8a1 100644 --- a/packages/ipfs-core/src/components/bootstrap/rm.js +++ b/packages/ipfs-core/src/components/bootstrap/rm.js @@ -4,7 +4,8 @@ const { isValidMultiaddr } = require('./utils') const { withTimeoutOption } = require('../../utils') /** - * @param {import('..').IPFSRepo} repo + * @param {Object} config + * @param {import('.').Repo} config.repo */ module.exports = ({ repo }) => { /** @@ -29,7 +30,7 @@ module.exports = ({ repo }) => { const config = await repo.config.getAll(options) config.Bootstrap = (config.Bootstrap || []).filter(ma => ma.toString() !== multiaddr.toString()) - await repo.config.set(config) + await repo.config.replace(config) return { Peers: [multiaddr] } } @@ -38,8 +39,7 @@ module.exports = ({ repo }) => { } /** - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').Multiaddr} Multiaddr * @typedef {import('./utils').Peers} Peers - * @typedef {import('..').CID} CID - * @typedef {import('..').Multiaddr} Multiaddr */ diff --git a/packages/ipfs-core/src/components/config.js b/packages/ipfs-core/src/components/config.js index b3df1c6f03..7584eb3bdf 100644 --- a/packages/ipfs-core/src/components/config.js +++ b/packages/ipfs-core/src/components/config.js @@ -6,27 +6,58 @@ const log = require('debug')('ipfs:core:config') /** * @param {Object} config - * @param {import('.').IPFSRepo} config.repo - * @returns {Config} + * @param {import('.').Repo} config.repo */ module.exports = ({ repo }) => { return { - getAll: withTimeoutOption(repo.config.getAll), - get: withTimeoutOption((key, options) => { - if (!key) { - return Promise.reject(new Error('key argument is required')) - } - - return repo.config.get(key, options) - }), - set: withTimeoutOption(repo.config.set), - replace: withTimeoutOption(repo.config.replace), + getAll: withTimeoutOption(getAll), + get: withTimeoutOption(get), + set: withTimeoutOption(set), + replace: withTimeoutOption(replace), profiles: { apply: withTimeoutOption(applyProfile), list: withTimeoutOption(listProfiles) } } + /** + * @param {AbortOptions} [options] + */ + async function getAll (options = {}) { + return await repo.config.getAll() + } + + /** + * + * @param {string} key + * @param {AbortOptions} [options] + */ + async function get (key, options) { + if (!key) { + return Promise.reject(new Error('key argument is required')) + } + + return await repo.config.get(key, options) + } + + /** + * + * @param {string} key + * @param {ToJSON} value + * @param {AbortOptions} [options] + */ + async function set (key, value, options) { + return await repo.config.set(key, value, options) + } + + /** + * @param {IPFSConfig} value + * @param {AbortOptions} [options] + */ + async function replace (value, options) { + return await repo.config.replace(value, options) + } + /** * @param {string} profileName * @param {*} options @@ -51,6 +82,7 @@ module.exports = ({ repo }) => { } // Scrub private key from output + // @ts-ignore `oldCfg.Identity` maybe undefined delete oldCfg.Identity.PrivKey delete newCfg.Identity.PrivKey @@ -190,10 +222,10 @@ module.exports.profiles = profiles * Returns the currently being used config. If the daemon is off, it returns * the stored config. * - * @param {string} [key] - The key of the value that should be fetched from the + * @param {string} key - The key of the value that should be fetched from the * config file. If no key is passed, then the whole config will be returned. * @param {AbortOptions} [options] - * @returns {Promise} - An object containing the configuration of the IPFS node + * @returns {Promise} - An object containing the configuration of the IPFS node * @example * const config = await ipfs.config.get('Addresses.Swarm') * console.log(config) @@ -216,7 +248,7 @@ module.exports.profiles = profiles * an effect. * * @param {string} key - The key of the value that should be added or replaced. - * @param {JSON} value - The value to be set. + * @param {ToJSON} value - The value to be set. * @param {AbortOptions} [options] * @returns {Promise} - Promise succeeds if config change succeeded, * otherwise fails with error. @@ -231,7 +263,7 @@ module.exports.profiles = profiles * i.e: if a config.replace changes the multiaddrs of the Swarm, Swarm will * have to be restarted manually for the changes to take an effect. * - * @param {Partial} value - A new configuration. + * @param {IPFSConfig} value - A new configuration. * @param {AbortOptions} [options] * @returns {Promise} * @example @@ -262,16 +294,13 @@ module.exports.profiles = profiles * @callback ApplyProfile * List available config profiles * @param {string} name - * @param {ApplyOptions} [options] + * @param {ApplyOptions & AbortOptions} [options] * @returns {Promise<{original: IPFSConfig, updated: IPFSConfig}>} * - * @typedef {Object} ApplyOptionsExt + * @typedef {Object} ApplyOptions * @property {boolean} [dryRun=false] - If true does not apply the profile - * @typedef {AbortOptions & ApplyOptionsExt} ApplyOptions * * - * @typedef {import('../utils').AbortOptions} AbortOptions - * * @typedef {Object} IPFSConfig * @property {AddressConfig} Addresses * @property {string} [Profiles] @@ -471,4 +500,7 @@ module.exports.profiles = profiles * exceeded, will trigger a connection GC operation. * * {{LowWater?:number, HighWater?:number}} ConnMgr + * + * @typedef {import('../interface/basic').ToJSON} ToJSON + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/core.js b/packages/ipfs-core/src/components/core.js new file mode 100644 index 0000000000..69734f94f6 --- /dev/null +++ b/packages/ipfs-core/src/components/core.js @@ -0,0 +1,45 @@ +'use strict' + +const createAddAPI = require('./add') +const createAddAllAPI = require('./add-all') +const createCatAPI = require('./cat') +const createGetAPI = require('./get') +const createLsAPI = require('./ls') + +class CoreAPI { + /** + * @param {Object} config + * @param {Block} config.block + * @param {Pin} config.pin + * @param {GCLock} config.gcLock + * @param {Preload} config.preload + * @param {IPLD} config.ipld + * @param {ShardingOptions} [config.options] + */ + constructor ({ preload, gcLock, pin, block, ipld, options }) { + const addAll = createAddAllAPI({ + preload, + gcLock, + block, + pin, + options + }) + + this.addAll = addAll + this.add = createAddAPI({ addAll }) + this.cat = createCatAPI({ ipld, preload }) + this.get = createGetAPI({ ipld, preload }) + this.ls = createLsAPI({ ipld, preload }) + } +} + +module.exports = CoreAPI + +/** + * @typedef {import('.').Block} Block + * @typedef {import('.').Pin} Pin + * @typedef {import('.').GCLock} GCLock + * @typedef {import('.').IPLD} IPLD + * @typedef {import('.').Preload} Preload + * @typedef {import('./add-all').ShardingOptions} ShardingOptions + */ diff --git a/packages/ipfs-core/src/components/dag/get.js b/packages/ipfs-core/src/components/dag/get.js index b0b305ea91..5aecec6188 100644 --- a/packages/ipfs-core/src/components/dag/get.js +++ b/packages/ipfs-core/src/components/dag/get.js @@ -14,20 +14,16 @@ module.exports = ({ ipld, preload }) => { /** * Retrieve an IPLD format node * - * @param {CID} ipfsPath - A DAG node that follows one of the supported IPLD formats - * @param {GetOptions & AbortOptions} [options] - An optional configration - * @returns {Promise} * @example * ```js - * ```JavaScript * // example obj * const obj = { - * a: 1, - * b: [1, 2, 3], - * c: { - * ca: [5, 6, 7], - * cb: 'foo' - * } + * a: 1, + * b: [1, 2, 3], + * c: { + * ca: [5, 6, 7], + * cb: 'foo' + * } * } * * const cid = await ipfs.dag.put(obj, { format: 'dag-cbor', hashAlg: 'sha2-256' }) @@ -35,8 +31,8 @@ module.exports = ({ ipld, preload }) => { * // zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5 * * async function getAndLog(cid, path) { - * const result = await ipfs.dag.get(cid, { path }) - * console.log(result.value) + * const result = await ipfs.dag.get(cid, { path }) + * console.log(result.value) * } * * await getAndLog(cid, '/a') @@ -58,6 +54,11 @@ module.exports = ({ ipld, preload }) => { * // Logs: * // 6 * ``` + * + * @param {CID|string} ipfsPath - A DAG node that follows one of the supported IPLD formats + * @param ipfsPath + * @param {GetOptions & AbortOptions} [options] - An optional configration + * @returns {Promise} */ const get = async function get (ipfsPath, options = {}) { const { @@ -74,11 +75,11 @@ module.exports = ({ ipld, preload }) => { } if (options.path) { - const result = options.localResolve - /** @type {DagEntry} - first will return undefined if empty */ - ? (await first(ipld.resolve(cid, options.path))) - /** @type {DagEntry} - last will return undefined if empty */ - : (await last(ipld.resolve(cid, options.path))) + const entry = options.localResolve + ? await first(ipld.resolve(cid, options.path)) + : await last(ipld.resolve(cid, options.path)) + /** @type {DagEntry} - first and last will return undefined when empty */ + const result = (entry) return result } @@ -102,6 +103,6 @@ module.exports = ({ ipld, preload }) => { * @property {Object} value * @property {string} remainderPath * - * @typedef {import('..').CID} CID - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/dag/index.js b/packages/ipfs-core/src/components/dag/index.js new file mode 100644 index 0000000000..31e0c3b634 --- /dev/null +++ b/packages/ipfs-core/src/components/dag/index.js @@ -0,0 +1,69 @@ +'use strict' + +const createGet = require('./get') +const createResolve = require('./resolve') +const createTree = require('./tree') +const createPut = require('./put') + +class Reader { + /** + * @param {ReaderConfig} config + */ + constructor (config) { + this.get = createGet(config) + this.resolve = createResolve(config) + this.tree = createTree(config) + } +} + +class DagAPI { + /** + * @param {Object} config + * @param {IPLD} config.ipld + * @param {Preload} config.preload + * @param {Pin} config.pin + * @param {GCLock} config.gcLock + * @param {DagReader} config.dagReader + */ + constructor ({ ipld, pin, preload, gcLock, dagReader }) { + const { get, resolve, tree } = dagReader + const put = createPut({ ipld, preload, pin, gcLock }) + + this.get = get + this.resolve = resolve + this.tree = tree + this.put = put + } + + /** + * Creates a reader part of the DAG API. This allows other APIs that require + * reader parts of the DAG API to be instantiated before components required + * by writer end are. + * + * @param {ReaderConfig} config + * @returns {DagReader} + */ + static reader (config) { + return new Reader(config) + } +} + +module.exports = DagAPI + +/** + * @typedef {Object} DagReader + * @property {ReturnType} get + * @property {ReturnType} resolve + * @property {ReturnType} tree + * + * @typedef {Object} ReaderConfig + * @property {IPLD} config.ipld + * @property {Preload} config.preload + * + * @typedef {import('..').IPLD} IPLD + * @typedef {import('..').Preload} Preload + * @typedef {import('..').Pin} Pin + * @typedef {import('..').GCLock} GCLock + * @typedef {import('..').CID} CID + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/dag/put.js b/packages/ipfs-core/src/components/dag/put.js index dc2e786f1d..e15fbea195 100644 --- a/packages/ipfs-core/src/components/dag/put.js +++ b/packages/ipfs-core/src/components/dag/put.js @@ -11,12 +11,12 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('..').IPLD} config.ipld - * @param {import("..").Pin} config.pin - * @param {import("..").GCLock} config.gcLock - * @param {import("..").Preload} config.preload + * @param {import('.').Pin} config.pin + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + * @param {import('.').GCLock} config.gcLock */ -module.exports = ({ ipld, pin, gcLock, preload }) => { +module.exports = ({ ipld, preload, pin, gcLock }) => { /** * Store an IPLD format node * @@ -32,49 +32,15 @@ module.exports = ({ ipld, pin, gcLock, preload }) => { * // zBwWX9ecx5F4X54WAjmFLErnBT6ByfNxStr5ovowTL7AhaUR98RWvXPS1V3HqV1qs3r5Ec5ocv7eCdbqYQREXNUfYNuKG * ``` */ - // eslint-disable-next-line complexity async function put (dagNode, options = {}) { - if (options.cid && (options.format || options.hashAlg)) { - throw new Error('Can\'t put dag node. Please provide either `cid` OR `format` and `hashAlg` options.') - } else if (((options.format && !options.hashAlg) || (!options.format && options.hashAlg))) { - throw new Error('Can\'t put dag node. Please provide `format` AND `hashAlg` options.') - } - - const optionDefaults = { - format: multicodec.DAG_CBOR, - hashAlg: multicodec.SHA2_256 - } + const { cidVersion, format, hashAlg } = readEncodingOptions(options) - // The IPLD expects the format and hashAlg as constants - if (options.format && typeof options.format === 'string') { - options.format = nameToCodec(options.format) - } - if (options.hashAlg && typeof options.hashAlg === 'string') { - options.hashAlg = nameToCodec(options.hashAlg) - } - - options = options.cid ? options : Object.assign({}, optionDefaults, options) - - // js-ipld defaults to verion 1 CIDs. Hence set version 0 explicitly for - // dag-pb nodes - if (options.version === undefined) { - if (options.format === multicodec.DAG_PB && options.hashAlg === multicodec.SHA2_256) { - options.version = 0 - } else { - options.version = 1 - } - } - - let release - - if (options.pin) { - release = await gcLock.readLock() - } + const release = options.pin ? await gcLock.readLock() : null try { - const cid = await ipld.put(dagNode, options.format, { - hashAlg: options.hashAlg, - cidVersion: options.version, + const cid = await ipld.put(dagNode, format, { + hashAlg, + cidVersion, signal: options.signal }) @@ -100,15 +66,79 @@ module.exports = ({ ipld, pin, gcLock, preload }) => { } /** - * @typedef {Object} PutOptions + * + * @param {PutOptions} options + */ +const readEncodingOptions = (options) => { + if (options.cid && (options.format || options.hashAlg)) { + throw new Error('Can\'t put dag node. Please provide either `cid` OR `format` and `hashAlg` options.') + } else if (((options.format && !options.hashAlg) || (!options.format && options.hashAlg))) { + throw new Error('Can\'t put dag node. Please provide `format` AND `hashAlg` options.') + } + + const cidVersion = readVersion(options) + + const { hashAlg, format } = options.cid != null + ? { format: options.cid.code, hashAlg: undefined } + : { ...defaultCIDOptions, ...options } + + return { + cidVersion, + format: typeof format === 'string' ? nameToCodec(format) : format, + hashAlg: typeof hashAlg === 'string' ? nameToCodec(hashAlg) : hashAlg + } +} + +/** + * Figures out what version of CID should be used given the options. + * + * @param {PutOptions} options + */ +const readVersion = ({ version, cid, format, hashAlg }) => { + // If version is passed just use that. + if (typeof version === 'number') { + return version + // If cid is provided use version field from it. + } else if (cid) { + return cid.version + // If it's dag-pb nodes use version 0 + } else if (format === multicodec.DAG_PB && hashAlg === multicodec.SHA2_256) { + return 0 + } else { + // Otherwise use version 1 + return 1 + } +} + +/** @type {WithCIDOptions} */ +const defaultCIDOptions = { + format: multicodec.DAG_CBOR, + hashAlg: multicodec.SHA2_256 +} + +/** + * @typedef {PutWith & OtherPutOptions} PutOptions + * @typedef {WithCID | WithCIDOptions} PutWith + * + * + * @typedef {Object} WithCID * @property {CID} [cid] - * @property {string|number} [format] - * @property {string|number} [hashAlg] + * // Note: We still stil need to reserve these fields otherwise it implies + * // that those fields can still be there and have very different types. + * @property {undefined} [format] + * @property {undefined} [hashAlg] + * @property {undefined} [version] + * + * @typedef {Object} WithCIDOptions + * @property {undefined} [cid] + * @property {string|number} format + * @property {string|number} hashAlg + * @property {0|1} [version] * + * @typedef {Object} OtherPutOptions * @property {boolean} [pin=false] - * @property {number} [version] * @property {boolean} [preload=false] * - * @typedef {import('..').CID} CID - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/dag/resolve.js b/packages/ipfs-core/src/components/dag/resolve.js index 30a7809e9f..eb7529bd1b 100644 --- a/packages/ipfs-core/src/components/dag/resolve.js +++ b/packages/ipfs-core/src/components/dag/resolve.js @@ -6,8 +6,8 @@ const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') /** * @param {Object} config - * @param {import('..').IPLD} config.ipld - * @param {import('..').Preload} config.preload + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload */ module.exports = ({ ipld, preload }) => { /** @@ -103,6 +103,5 @@ module.exports = ({ ipld, preload }) => { * @property {string} remainderPath - The path to the end of the IPFS path * inside the node referenced by the CID * - * @typedef {import('..').CID} CID - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/dag/tree.js b/packages/ipfs-core/src/components/dag/tree.js index 3beedd768e..1c8fa3e41e 100644 --- a/packages/ipfs-core/src/components/dag/tree.js +++ b/packages/ipfs-core/src/components/dag/tree.js @@ -5,8 +5,8 @@ const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') /** * @param {Object} config - * @param {import('..').IPLD} config.ipld - * @param {import("..").Preload} config.preload + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload */ module.exports = ({ ipld, preload }) => { /** @@ -47,7 +47,7 @@ module.exports = ({ ipld, preload }) => { * // c/cb * ``` */ - async function * tree (ipfsPath, options = {}) { // eslint-disable-line require-await + async function * tree (ipfsPath, options = {}) { const { cid, path @@ -77,6 +77,6 @@ module.exports = ({ ipld, preload }) => { * @property {string} remainderPath - The path to the end of the IPFS path * inside the node referenced by the CID * - * @typedef {import('..').CID} CID - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/dht.js b/packages/ipfs-core/src/components/dht.js index f53865dabb..24180f3ffa 100644 --- a/packages/ipfs-core/src/components/dht.js +++ b/packages/ipfs-core/src/components/dht.js @@ -4,8 +4,15 @@ const PeerId = require('peer-id') const CID = require('cids') const errCode = require('err-code') const { withTimeoutOption } = require('../utils') +const { NotEnabledError } = require('../errors') +const get = require('dlv') -module.exports = ({ libp2p, repo }) => { +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + * @param {import('.').Repo} config.repo + */ +module.exports = ({ network, repo }) => { const { get, put, findProvs, findPeer, provide, query } = { /** * Given a key, query the DHT for its best value. @@ -14,7 +21,8 @@ module.exports = ({ libp2p, repo }) => { * @param {AbortOptions} [options] - The key associated with the value to find * @returns {Promise} */ - async get (key, options = {}) { // eslint-disable-line require-await + async get (key, options = {}) { + const { libp2p } = await use(network, options) return libp2p._dht.get(normalizeCID(key), options) }, @@ -30,8 +38,9 @@ module.exports = ({ libp2p, repo }) => { * @param {AbortOptions} [options] * @returns {AsyncIterable} */ - put (key, value, options) { - return libp2p._dht.put(normalizeCID(key), value) + async * put (key, value, options) { + const { libp2p } = await use(network, options) + yield * libp2p._dht.put(normalizeCID(key), value) }, /** @@ -50,6 +59,7 @@ module.exports = ({ libp2p, repo }) => { * ``` */ async * findProvs (cid, options = {}) { + const { libp2p } = await use(network, options) if (options.numProviders) { options.maxNumProviders = options.numProviders } @@ -83,7 +93,8 @@ module.exports = ({ libp2p, repo }) => { * // '/ip4/147.75.94.115/tcp/4001' * ``` */ - async findPeer (peerId, options) { // eslint-disable-line require-await + async findPeer (peerId, options) { + const { libp2p } = await use(network, options) if (typeof peerId === 'string') { peerId = PeerId.createFromCID(peerId) } @@ -104,6 +115,7 @@ module.exports = ({ libp2p, repo }) => { * @returns {AsyncIterable} */ async * provide (cids, options = {}) { + const { libp2p } = await use(network, options) cids = Array.isArray(cids) ? cids : [cids] for (var i in cids) { @@ -142,6 +154,7 @@ module.exports = ({ libp2p, repo }) => { * @returns {AsyncIterable<{ id: CID, addrs: Multiaddr[] }>} */ async * query (peerId, options) { + const { libp2p } = await use(network, options) if (typeof peerId === 'string') { peerId = PeerId.createFromCID(peerId) } @@ -191,6 +204,18 @@ const parseCID = cid => { const normalizeCID = cid => cid instanceof Uint8Array ? cid : parseCID(cid) +/** + * @param {import('.').NetworkService} network + * @param {AbortOptions} [options] + */ +const use = async (network, options) => { + const net = await network.use(options) + if (get(net.libp2p, '_config.dht.enabled', false)) { + return net + } else { + throw new NotEnabledError('dht not enabled') + } +} /** * @typedef {Object} QueryEvent * @property {PeerId} id diff --git a/packages/ipfs-core/src/components/files/index.js b/packages/ipfs-core/src/components/files/index.js index e5f29180de..864590ed03 100644 --- a/packages/ipfs-core/src/components/files/index.js +++ b/packages/ipfs-core/src/components/files/index.js @@ -94,11 +94,11 @@ function createMfs (options) { /** * @param {Object} context * @param {import('..').IPLD} context.ipld - * @param {import('..').IPLDBlock} context.block - * @param {import('..').IPFSBlockService} context.blockService - * @param {import('..').IPFSRepo} context.repo + * @param {import('..').Block} context.block + * @param {import('..').BlockService} context.blockService + * @param {import('..').Repo} context.repo * @param {import('..').Preload} context.preload - * @param {import('../init').ConstructorOptions} context.options + * @param {import('..').Options} context.options * @returns {MFS} */ module.exports = ({ ipld, block, blockService, repo, preload, options: constructorOptions }) => { diff --git a/packages/ipfs-core/src/components/files/ls.js b/packages/ipfs-core/src/components/files/ls.js index 4610c34763..195002025f 100644 --- a/packages/ipfs-core/src/components/files/ls.js +++ b/packages/ipfs-core/src/components/files/ls.js @@ -3,6 +3,7 @@ const exporter = require('ipfs-unixfs-exporter') const toMfsPath = require('./utils/to-mfs-path') const { + MFS_FILE_TYPES, withTimeoutOption } = require('../../utils') @@ -11,20 +12,14 @@ const { * @returns {UnixFSEntry} */ const toOutput = (fsEntry) => { - /** @type FileType */ - let type = 'file' + let type = 0 let size = fsEntry.node.size || fsEntry.node.length let mode let mtime if (fsEntry.unixfs) { size = fsEntry.unixfs.fileSize() - type = fsEntry.unixfs.type - - if (fsEntry.unixfs.type === 'hamt-sharded-directory') { - type = 'directory' - } - + type = MFS_FILE_TYPES[fsEntry.unixfs.type] mode = fsEntry.unixfs.mode mtime = fsEntry.unixfs.mtime } @@ -94,13 +89,10 @@ module.exports = (context) => { * @property {number} [nsecs] - the number of nanoseconds since the last full * second. * - * @typedef {'file'|'directory'} FileType - * * @typedef {object} UnixFSEntry * @property {CID} cid - * @property {string} name * @property {number} [mode] * @property {UnixTimeObj} [mtime] * @property {number} size - * @property {FileType} type + * @property {number} type */ diff --git a/packages/ipfs-core/src/components/gc-lock.js b/packages/ipfs-core/src/components/gc-lock.js new file mode 100644 index 0000000000..9eed02de1c --- /dev/null +++ b/packages/ipfs-core/src/components/gc-lock.js @@ -0,0 +1,24 @@ +'use strict' + +const mortice = require('mortice') + +/** + * @param {Object} config + * @param {string} config.path + * @param {boolean} [config.repoOwner] + * @returns {GCLock} + */ +module.exports = ({ path, repoOwner }) => + mortice(path, { + singleProcess: repoOwner !== false + }) + +/** + * @typedef {RWLock} GCLock + * + * @typedef {Object} RWLock + * @property {() => Promise} readLock + * @property {() => Promise} writeLock + * + * @typedef {() => void} Lock + */ diff --git a/packages/ipfs-core/src/components/id.js b/packages/ipfs-core/src/components/id.js index 226dcc239e..62afd8d6e5 100644 --- a/packages/ipfs-core/src/components/id.js +++ b/packages/ipfs-core/src/components/id.js @@ -7,15 +7,15 @@ const uint8ArrayToString = require('uint8arrays/to-string') /** * @param {Object} config - * @param {import('peer-id')} config.peerId - * @param {import('libp2p')} [config.libp2p] + * @param {import('.').PeerId} config.peerId + * @param {import('.').NetworkService} config.network */ -module.exports = ({ peerId, libp2p }) => { +module.exports = ({ network, peerId }) => { /** * Returns the identity of the Peer * * @param {import('../utils').AbortOptions} [_options] - * @returns {Promise} + * @returns {Promise} * @example * ```js * const identity = await ipfs.id() @@ -27,7 +27,10 @@ module.exports = ({ peerId, libp2p }) => { let addresses = [] let protocols = [] - if (libp2p) { + const net = network.try() + + if (net) { + const { libp2p } = net // only available while the node is running addresses = libp2p.transportManager.getAddrs() protocols = Array.from(libp2p.upgrader.protocols.keys()) @@ -59,7 +62,7 @@ module.exports = ({ peerId, libp2p }) => { } /** - * @typedef {object} PeerId + * @typedef {object} ID * The Peer identity * @property {string} id - the Peer ID * @property {string} publicKey - the public key of the peer as a base64 encoded string diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index fd4f8c1794..b52fbc54a4 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -1,322 +1,377 @@ 'use strict' -/** - * @typedef {ReturnType} Add - */ -exports.add = require('./add') - -/** - * @typedef {ReturnType} AddAll - */ - -exports.addAll = require('./add-all') - -/** - * @typedef {Object} Block - * @property {ReturnType} get - * @property {ReturnType} put - * @property {ReturnType} rm - * @property {ReturnType} stat - */ -exports.block = { - get: require('./block/get'), - put: require('./block/put'), - rm: require('./block/rm'), - stat: require('./block/stat') +const { mergeOptions } = require('../utils') +const { isTest } = require('ipfs-utils/src/env') +const log = require('debug')('ipfs') + +const { DAGNode } = require('ipld-dag-pb') +const UnixFs = require('ipfs-unixfs') +const multicodec = require('multicodec') +const initAssets = require('../runtime/init-assets-nodejs') + +const createStartAPI = require('./start') +const createStopAPI = require('./stop') +const createDNSAPI = require('./dns') +const createIsOnlineAPI = require('./is-online') +const createResolveAPI = require('./resolve') +const PinAPI = require('./pin') +const IPNSAPI = require('./ipns') +const NameAPI = require('./name') +const createRefsAPI = require('./refs') +const createRefsLocalAPI = require('./refs/local') +const BitswapAPI = require('./bitswap') +const BootstrapAPI = require('./bootstrap') +const BlockAPI = require('./block') +const CoreAPI = require('./core') +const createVersionAPI = require('./version') +const createIDAPI = require('./id') +const createConfigAPI = require('./config') +const DagAPI = require('./dag') +const PinManagerAPI = require('./pin/pin-manager') +const createPreloadAPI = require('../preload') +const createMfsPreloadAPI = require('../mfs-preload') +const createFilesAPI = require('./files') +const KeyAPI = require('./key') +const ObjectAPI = require('./object') +const RepoAPI = require('./repo') +const StatsAPI = require('./stats') +const IPFSBlockService = require('ipfs-block-service') +const createIPLD = require('./ipld') +const Storage = require('./storage') +const Network = require('./network') +const Service = require('../utils/service') +const SwarmAPI = require('./swarm') +const createGCLockAPI = require('./gc-lock') +const createPingAPI = require('./ping') +const createDHTAPI = require('./dht') +const createPubSubAPI = require('./pubsub') + +class IPFS { + /** + * @param {Object} config + * @param {Print} config.print + * @param {StorageAPI} config.storage + * @param {Options} config.options + */ + constructor ({ print, storage, options }) { + const { peerId, repo, keychain, pass } = storage + const network = Service.create(Network) + + const preload = createPreloadAPI(options.preload) + + /** @type {BlockService} */ + const blockService = new IPFSBlockService(storage.repo) + const ipld = createIPLD({ blockService, print, options: options.ipld }) + + const gcLock = createGCLockAPI({ + path: repo.path, + repoOwner: options.repoOwner + }) + const dns = createDNSAPI() + const isOnline = createIsOnlineAPI({ network }) + const ipns = new IPNSAPI() + const dagReader = DagAPI.reader({ ipld, preload }) + + const name = new NameAPI({ + dns, + ipns, + dagReader, + peerId, + isOnline, + keychain, + options + }) + const resolve = createResolveAPI({ ipld, name }) + const pinManager = new PinManagerAPI({ repo, dagReader }) + const pin = new PinAPI({ gcLock, pinManager, dagReader }) + const block = new BlockAPI({ blockService, preload, gcLock, pinManager, pin }) + const dag = new DagAPI({ ipld, preload, gcLock, pin, dagReader }) + const refs = Object.assign(createRefsAPI({ ipld, resolve, preload }), { + local: createRefsLocalAPI({ repo: storage.repo }) + }) + const { add, addAll, cat, get, ls } = new CoreAPI({ + gcLock, + preload, + pin, + block, + ipld, + options: options.EXPERIMENTAL + }) + + const files = createFilesAPI({ + ipld, + block, + blockService, + repo, + preload, + options + }) + + const mfsPreload = createMfsPreloadAPI({ + files, + preload, + options: options.preload + }) + + this.preload = preload + this.name = name + this.ipld = ipld + this.ipns = ipns + this.pin = pin + this.resolve = resolve + this.block = block + this.refs = refs + + this.start = createStartAPI({ + network, + peerId, + repo, + blockService, + preload, + ipns, + mfsPreload, + print, + keychain, + pass + }) + + this.stop = createStopAPI({ + network, + preload, + mfsPreload, + blockService, + ipns, + repo + }) + + this.dht = createDHTAPI({ network, repo }) + this.pubsub = createPubSubAPI({ network, config: options.config }) + this.dns = dns + this.isOnline = isOnline + this.id = createIDAPI({ network, peerId }) + this.version = createVersionAPI({ repo }) + this.bitswap = new BitswapAPI({ network }) + this.bootstrap = new BootstrapAPI({ repo }) + this.config = createConfigAPI({ repo }) + this.ping = createPingAPI({ network }) + + this.add = add + this.addAll = addAll + this.cat = cat + this.get = get + this.ls = ls + + this.dag = dag + this.files = files + this.key = new KeyAPI({ keychain }) + this.object = new ObjectAPI({ ipld, preload, gcLock, dag }) + this.repo = new RepoAPI({ gcLock, pin, repo, refs }) + this.stats = new StatsAPI({ repo, network }) + this.swarm = new SwarmAPI({ network }) + + // For the backwards compatibility + Object.defineProperty(this, 'libp2p', () => { + const net = network.try() + return net ? net.libp2p : undefined + }) + } + + async init () { + // Just keep this around for backwards compatibility + } + + /** + * @param {Options} options + */ + static async create (options = {}) { + options = mergeOptions(getDefaultOptions(), options) + + // eslint-disable-next-line no-console + const print = options.silent ? log : console.log + + const init = { + ...mergeOptions(initOptions(options), options), + print + } + + const storage = await Storage.start(init) + const config = await storage.repo.config.getAll() + + const ipfs = new IPFS({ + storage, + print, + options: { ...options, config } + }) + + await ipfs.preload.start() + + ipfs.ipns.startOffline(storage) + if (!storage.isInitialized && !init.emptyRepo) { + // add empty unixfs dir object (go-ipfs assumes this exists) + const cid = await addEmptyDir(ipfs) + + log('adding default assets') + await initAssets({ addAll: ipfs.addAll, print }) + + log('initializing IPNS keyspace') + await ipfs.ipns.initializeKeyspace(storage.peerId.privKey, cid.toString()) + } + + if (options.start !== false) { + await ipfs.start() + } + + return ipfs + } } +module.exports = IPFS /** - * @typedef {Object} BitSwap - * @property {ReturnType} stat - * @property {ReturnType} unwant - * @property {ReturnType} wantlist + * @param {Options} options + * @returns {InitOptions} */ -exports.bitswap = { - stat: require('./bitswap/stat'), - unwant: require('./bitswap/unwant'), - wantlist: require('./bitswap/wantlist'), - wantlistForPeer: require('./bitswap/wantlist-for-peer') -} +const initOptions = ({ init }) => + init === 'object' ? init : {} /** - * @typedef {Object} Bootstrap - * @property {ReturnType} add - * @property {ReturnType} list - * @property {ReturnType} rm + * @param {IPFS} ipfs */ -exports.bootstrap = { - add: require('./bootstrap/add'), - clear: require('./bootstrap/clear'), - list: require('./bootstrap/list'), - reset: require('./bootstrap/reset'), - rm: require('./bootstrap/rm') +const addEmptyDir = async (ipfs) => { + const node = new DAGNode(new UnixFs('directory').marshal()) + const cid = await ipfs.dag.put(node, { + version: 0, + format: multicodec.DAG_PB, + hashAlg: multicodec.SHA2_256, + preload: false + }) + + await ipfs.pin.add(cid) + + return cid } /** - * @typedef {ReturnType} Cat + * @returns {Options} */ -exports.cat = require('./cat') +const getDefaultOptions = () => ({ + init: true, + start: true, + EXPERIMENTAL: {}, + preload: { + enabled: !isTest, // preload by default, unless in test env + addresses: [ + '/dns4/node0.preload.ipfs.io/https', + '/dns4/node1.preload.ipfs.io/https', + '/dns4/node2.preload.ipfs.io/https', + '/dns4/node3.preload.ipfs.io/https' + ] + } +}) /** - * @typedef {ReturnType} Config - */ -exports.config = require('./config') - -/** - * @typedef {Object} DAG - * @property {ReturnType} get - * @property {ReturnType} put - * @property {ReturnType} resolve - * @property {ReturnType} tree - */ -exports.dag = { - get: require('./dag/get'), - put: require('./dag/put'), - resolve: require('./dag/resolve'), - tree: require('./dag/tree') -} - -/** @typedef {ReturnType} DHT */ -exports.dht = require('./dht') - -/** @typedef {ReturnType} DNS */ -exports.dns = require('./dns') - -/** @typedef {ReturnType} Files */ -exports.files = require('./files') - -/** @typedef {ReturnType} Get */ -exports.get = require('./get') - -/** @typedef {ReturnType} ID */ -exports.id = require('./id') - -/** @typedef {ReturnType} Init */ -exports.init = require('./init') - -/** @typedef {ReturnType} IsOnline */ -exports.isOnline = require('./is-online') - -/** - * @typedef {Object} Key - * @property {ReturnType} export - * @property {ReturnType} gen - * @property {ReturnType} import - * @property {ReturnType} info - * @property {ReturnType} list - * @property {ReturnType} rename - * @property {ReturnType} rm - */ - -exports.key = { - export: require('./key/export'), - gen: require('./key/gen'), - import: require('./key/import'), - info: require('./key/info'), - list: require('./key/list'), - rename: require('./key/rename'), - rm: require('./key/rm') -} - -/** @typedef {ReturnType} LibP2P */ -exports.libp2p = require('./libp2p') - -/** @typedef {ReturnType} LS */ -exports.ls = require('./ls') - -/** - * @typedef {Object} Name - * @property {ReturnType} publish - * @property {ReturnType} resolve - * @property {NamePubSub} pubsub + * @typedef {StorageOptions & IPFSOptions} Options * - * @typedef {Object} NamePubSub - * @property {ReturnType} cancel - * @property {ReturnType} state - * @property {ReturnType} subs - */ - -exports.name = { - publish: require('./name/publish'), - pubsub: { - cancel: require('./name/pubsub/cancel'), - state: require('./name/pubsub/state'), - subs: require('./name/pubsub/subs') - }, - resolve: require('./name/resolve') -} - -/** - * @typedef {Object} ObjectAPI - * @property {ReturnType} data - * @property {ReturnType} get - * @property {ReturnType} links - * @property {ReturnType} new - * @property {ReturnType} put - * @property {ReturnType} stat - * @property {ObjectPath} patch + * @typedef {Object} IPFSOptions + * Options argument can be used to specify advanced configuration. + * @property {InitOptions|boolean} [init=true] - Perform repo initialization steps when creating + * the IPFS node. + * Note that *initializing* a repo is different from creating an instance of + * [`ipfs.Repo`](https://github.com/ipfs/js-ipfs-repo). The IPFS constructor + * sets many special properties when initializing a repo, so you should usually + * not try and call `repoInstance.init()` yourself. + * @property {boolean} [start=true] - If `false`, do not automatically + * start the IPFS node. Instead, you’ll need to manually call + * [`node.start()`](https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs/docs/MODULE.md#nodestart) + * yourself. + * @property {string} [pass=null] - A passphrase to encrypt/decrypt your keys. + * @property {boolean} [silent=false] - Prevents all logging output from the + * IPFS node. (Default: `false`) + * @property {RelayOptions} [relay={ enabled: true, hop: { enabled: false, active: false } }] + * - Configure circuit relay (see the [circuit relay tutorial] + * (https://github.com/ipfs/js-ipfs/tree/master/examples/circuit-relaying) + * to learn more). + * @property {boolean} [offline=false] - Run ipfs node offline. The node does + * not connect to the rest of the network but provides a local API. + * @property {PreloadOptions} [preload] - Configure remote preload nodes. + * The remote will preload content added on this node, and also attempt to + * preload objects requested by this node. + * @property {ExperimentalOptions} [EXPERIMENTAL] - Enable and configure + * experimental features. + * @property {IPFSConfig} [config] - Modify the default IPFS node config. This + * object will be *merged* with the default config; it will not replace it. + * (Default: [`config-nodejs.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/config-nodejs.js) + * in Node.js, [`config-browser.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/config-browser.js) + * in browsers) + * @property {IPLDOptions} [ipld] - Modify the default IPLD config. This object + * will be *merged* with the default config; it will not replace it. Check IPLD + * [docs](https://github.com/ipld/js-ipld#ipld-constructor) for more information + * on the available options. (Default: [`ipld-nodejs.js`] + * (https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld-nodejs.js) in Node.js, [`ipld-browser.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld-browser.js) + * in browsers) + * @property {LibP2POptions|Function} [libp2p] - The libp2p option allows you to build + * your libp2p node by configuration, or via a bundle function. If you are + * looking to just modify the below options, using the object format is the + * quickest way to get the default features of libp2p. If you need to create a + * more customized libp2p node, such as with custom transports or peer/content + * routers that need some of the ipfs data on startup, a custom bundle is a + * great way to achieve this. + * - You can see the bundle in action in the [custom libp2p example](https://github.com/ipfs/js-ipfs/tree/master/examples/custom-libp2p). + * - Please see [libp2p/docs/CONFIGURATION.md](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md) + * for the list of options libp2p supports. + * - Default: [`libp2p-nodejs.js`](../src/core/runtime/libp2p-nodejs.js) + * in Node.js, [`libp2p-browser.js`](../src/core/runtime/libp2p-browser.js) in + * browsers. + * @property {boolean} [repoOwner] * - * @typedef {Object} ObjectPath - * @property {ReturnType} addLink - * @property {ReturnType} rmLink - * @property {ReturnType} appendData - * @property {ReturnType} setData - */ -exports.object = { - data: require('./object/data'), - get: require('./object/get'), - links: require('./object/links'), - new: require('./object/new'), - patch: { - addLink: require('./object/patch/add-link'), - appendData: require('./object/patch/append-data'), - rmLink: require('./object/patch/rm-link'), - setData: require('./object/patch/set-data') - }, - put: require('./object/put'), - stat: require('./object/stat') -} - -/** - * @typedef Pin - * @property {ReturnType} add - * @property {ReturnType} addAll - * @property {ReturnType} ls - * @property {ReturnType} rm - */ -exports.pin = { - add: require('./pin/add'), - addAll: require('./pin/add-all'), - ls: require('./pin/ls'), - rm: require('./pin/rm'), - rmAll: require('./pin/rm-all') -} - -/** - * @typedef {ReturnType} Ping - */ -exports.ping = require('./ping') - -/** - * @typedef {ReturnType} PubSub - */ -exports.pubsub = require('./pubsub') - -/** - * @typedef {ReturnType} Refs - * @typedef {ReturnType} LocalRefs - * @typedef {Refs & {local:LocalRefs}} RefsWithLocal - */ -exports.refs = Object.assign(require('./refs'), { local: require('./refs/local') }) - -/** - * @typedef {Object} Repo - * @property {ReturnType} gc - * @property {ReturnType} stat - * @property {ReturnType} version - */ -exports.repo = { - gc: require('./repo/gc'), - stat: require('./repo/stat'), - version: require('./repo/version') -} - -/** @typedef {ReturnType} Resolve */ -exports.resolve = require('./resolve') - -/** @typedef {ReturnType} Start */ -exports.start = require('./start') - -/** - * @typedef {Object} Stats - * @property {ReturnType} bw - */ -exports.stats = { - bw: require('./stats/bw') -} - -/** @typedef {ReturnType} Stop */ -exports.stop = require('./stop') - -/** - * @typedef {Object} Swarm - * @property {ReturnType} addrs - * @property {ReturnType} connect - * @property {ReturnType} disconnect - * @property {ReturnType} localAddrs - * @property {ReturnType} peers - */ -exports.swarm = { - addrs: require('./swarm/addrs'), - connect: require('./swarm/connect'), - disconnect: require('./swarm/disconnect'), - localAddrs: require('./swarm/local-addrs'), - peers: require('./swarm/peers') -} - -/** - * @typedef {ReturnType} Version - */ -exports.version = require('./version') - -/** - * @typedef {ReturnType} Preload - * @typedef {RWLock} GCLock + * @typedef {object} ExperimentalOptions + * @property {boolean} [ipnsPubsub] - Enable pub-sub on IPNS. (Default: `false`) + * @property {boolean} [sharding] - Enable directory sharding. Directories that have many child objects will be represented by multiple DAG nodes instead of just one. It can improve lookup performance when a directory has several thousand files or more. (Default: `false`) * - * @typedef {Object} RWLock - * @property {() => Promise} readLock - * @property {() => Promise} writeLock * - * @typedef {() => void} Lock + * @typedef {import('./storage').StorageOptions} StorageOptions + * @typedef {import('../preload').Options} PreloadOptions + * @typedef {import('./ipld').Options} IPLDOptions + * @typedef {import('./libp2p').Options} LibP2POptions * - * // External library types - * @typedef {import('cids')} CID - * @typedef {import('peer-id')} PeerId - * @typedef {import('multiaddr')} Multiaddr + * @typedef {object} RelayOptions + * @property {boolean} [enabled] - Enable circuit relay dialer and listener. (Default: `true`) + * @property {object} [hop] + * @property {boolean} [hop.enabled] - Make this node a relay (other nodes can connect *through* it). (Default: `false`) + * @property {boolean} [hop.active] - Make this an *active* relay node. Active relay nodes will attempt to dial a destin * - * // Justs pretending these things are typed & hopefully in the future they - * // wil be. - * @typedef {import('ipld')} IPLD - * @typedef {import('ipld').Config} IPLDConfig - * @typedef {import('ipld-block')} IPLDBlock - * @typedef {import('ipfs-repo')} IPFSRepo - * @typedef {import('ipfs-block-service')} IPFSBlockService - * @typedef {import('ipfs-bitswap')} IPFSBitSwap - * @typedef {import('libp2p')} LibP2PService - * @typedef {import('libp2p').Config} LibP2PConfig - */ - -/** - * @typedef {Object} IPFSAPI - * @property {Add} add - * @property {BitSwap} bitswap - * @property {Block} block - * @property {Bootstrap} bootstrap - * @property {Cat} cat - * @property {Config} config - * @property {DAG} dag - * @property {DHT} dht - * @property {DNS} dns - * @property {Files} files - * @property {Get} get - * @property {ID} id - * @property {IsOnline} isOnline - * @property {Key} key - * @property {LibP2P} libp2p - * @property {LS} ls - * @property {Name} name - * @property {ObjectAPI} object - * @property {Pin} pin - * @property {Ping} ping - * @property {PubSub} pubsub - * @property {Refs} refs - * @property {Repo} repo - * @property {Resolve} resolve - * @property {Stats} stats - * @property {Swarm} swarm - * @property {Version} version + * @typedef {import('./storage').InitOptions} InitOptions * - * @property {Init} init - * @property {Start} start - * @property {Stop} stop + * @typedef {import('./storage')} StorageAPI + * + * @typedef {import('./network').Options} NetworkOptions + * @typedef {Service} NetworkService + * @typedef {import('./storage').Repo} Repo + * @typedef {(...args:any[]) => void} Print + * @typedef {import('./storage').Keychain} Keychain + * @typedef {import('./config').IPFSConfig} IPFSConfig + * + * @typedef {import('peer-id')} PeerId + * @typedef {import('./libp2p').LibP2P} LibP2P + * @typedef {import('./pin/pin-manager')} PinManager + * @typedef {import('../interface/block-service').BlockService} BlockService + * @typedef {import('../interface/bitswap').Bitswap} BitSwap + * @typedef {import('./ipld').IPLD} IPLD + * @typedef {import('./gc-lock').GCLock} GCLock + * @typedef {import('../preload').Preload} Preload + * @typedef {import('../mfs-preload').MFSPreload} MFSPreload + * @typedef {import('./ipns')} IPNS + * @typedef {import('./pin')} Pin + * @typedef {import('./block')} Block + * @typedef {import('./dag').DagReader} DagReader + * @typedef {import('./dag')} Dag + * @typedef {ReturnType} Files + * @typedef {ReturnType} IsOnline + * @typedef {ReturnType} Resolve + * @typedef {ReturnType} Refs + * @typedef {ReturnType} DNS + * @typedef {import('./name')} Name + * @typedef {import('../utils').AbortOptions} AbortOptions + * @typedef {import('cids')} CID + * @typedef {import('multiaddr')} Multiaddr + * @typedef {import('./ipld').Block} IPLDBlock */ diff --git a/packages/ipfs-core/src/components/init.js b/packages/ipfs-core/src/components/init.js deleted file mode 100644 index 219d443e34..0000000000 --- a/packages/ipfs-core/src/components/init.js +++ /dev/null @@ -1,561 +0,0 @@ -'use strict' - -const log = require('debug')('ipfs:components:init') -const PeerId = require('peer-id') -const uint8ArrayFromString = require('uint8arrays/from-string') -const uint8ArrayToString = require('uint8arrays/to-string') - -const mergeOptions = require('merge-options') -const getDefaultConfig = require('../runtime/config-nodejs.js') -const createRepo = require('../runtime/repo-nodejs') -const mortice = require('mortice') -const { DAGNode } = require('ipld-dag-pb') -const UnixFs = require('ipfs-unixfs') -const multicodec = require('multicodec') -const { - AlreadyInitializingError, - AlreadyInitializedError, - NotStartedError, - NotEnabledError -} = require('../errors') -const BlockService = require('ipfs-block-service') - -/** - * @typedef {import('.').IPLD} IPLD - */ -const Ipld = require('ipld') -const getDefaultIpldOptions = require('../runtime/ipld') - -const createPreloader = require('../preload') -const { ERR_REPO_NOT_INITIALIZED } = require('ipfs-repo').errors -const IPNS = require('../ipns') -const OfflineDatastore = require('../ipns/routing/offline-datastore') -const initAssets = require('../runtime/init-assets-nodejs') -const PinManager = require('./pin/pin-manager') -const Components = require('./') - -/** - * @param {Object} config - * @param {import('../api-manager')} config.apiManager - * @param {(...args:any[]) => void} config.print - * @param {ConstructorOptions} config.options - */ -module.exports = ({ - apiManager, - print, - options: constructorOptions -}) => -/** - * @param {Object} options - */ - async function init (options = {}) { - const { cancel } = apiManager.update({ init: () => { throw new AlreadyInitializingError() } }) - - try { - if (typeof constructorOptions.init === 'object') { - options = mergeOptions(constructorOptions.init, options) - } - - options.pass = options.pass || constructorOptions.pass - - if (constructorOptions.config) { - options.config = mergeOptions(options.config, constructorOptions.config) - } - - options.repo = options.repo || constructorOptions.repo - options.repoAutoMigrate = options.repoAutoMigrate || constructorOptions.repoAutoMigrate - - const repo = typeof options.repo === 'string' || options.repo == null - ? createRepo({ path: options.repo, autoMigrate: options.repoAutoMigrate, silent: constructorOptions.silent }) - : options.repo - - let isInitialized = true - - if (repo.closed) { - try { - await repo.open() - } catch (err) { - if (err.code === ERR_REPO_NOT_INITIALIZED) { - isInitialized = false - } else { - throw err - } - } - } - - if (!isInitialized && options.allowNew === false) { - throw new NotEnabledError('new repo initialization is not enabled') - } - - const { peerId, keychain } = isInitialized - ? await initExistingRepo(repo, options) - : await initNewRepo(repo, { ...options, print }) - - log('peer created') - - const blockService = new BlockService(repo) - const ipld = new Ipld(getDefaultIpldOptions(blockService, constructorOptions.ipld, log)) - - const preload = createPreloader(constructorOptions.preload) - await preload.start() - - // Make sure GC lock is specific to repo, for tests where there are - // multiple instances of IPFS - const gcLock = mortice(repo.path, { singleProcess: constructorOptions.repoOwner !== false }) - const dag = { - get: Components.dag.get({ ipld, preload }), - resolve: Components.dag.resolve({ ipld, preload }), - tree: Components.dag.tree({ ipld, preload }), - // FIXME: resolve this circular dependency - get put () { - const put = Components.dag.put({ ipld, pin, gcLock, preload }) - Object.defineProperty(this, 'put', { value: put }) - return put - } - } - - const object = { - data: Components.object.data({ ipld, preload }), - get: Components.object.get({ ipld, preload }), - links: Components.object.links({ dag }), - new: Components.object.new({ ipld, preload }), - patch: { - addLink: Components.object.patch.addLink({ ipld, gcLock, preload }), - appendData: Components.object.patch.appendData({ ipld, gcLock, preload }), - rmLink: Components.object.patch.rmLink({ ipld, gcLock, preload }), - setData: Components.object.patch.setData({ ipld, gcLock, preload }) - }, - put: Components.object.put({ ipld, gcLock, preload }), - stat: Components.object.stat({ ipld, preload }) - } - - const pinManager = new PinManager(repo, dag) - const pinAddAll = Components.pin.addAll({ pinManager, gcLock, dag }) - const pinRmAll = Components.pin.rmAll({ pinManager, gcLock, dag }) - - const pin = { - add: Components.pin.add({ addAll: pinAddAll }), - addAll: pinAddAll, - ls: Components.pin.ls({ pinManager, dag }), - rm: Components.pin.rm({ rmAll: pinRmAll }), - rmAll: pinRmAll - } - - const block = { - get: Components.block.get({ blockService, preload }), - put: Components.block.put({ blockService, pin, gcLock, preload }), - rm: Components.block.rm({ blockService, gcLock, pinManager }), - stat: Components.block.stat({ blockService, preload }) - } - - const addAll = Components.addAll({ block, preload, pin, gcLock, options: constructorOptions }) - - if (!isInitialized && !options.emptyRepo) { - // add empty unixfs dir object (go-ipfs assumes this exists) - const emptyDirCid = await addEmptyDir({ dag, pin }) - - log('adding default assets') - await initAssets({ addAll, print }) - - log('initializing IPNS keyspace') - // Setup the offline routing for IPNS. - // This is primarily used for offline ipns modifications, such as the initializeKeyspace feature. - const offlineDatastore = new OfflineDatastore(repo) - const ipns = new IPNS(offlineDatastore, repo.datastore, peerId, keychain, { pass: options.pass }) - await ipns.initializeKeyspace(peerId.privKey, emptyDirCid.toString()) - } - - const api = createApi({ - add: Components.add({ addAll }), - addAll, - apiManager, - constructorOptions, - block, - blockService, - dag, - gcLock, - initOptions: options, - ipld, - keychain, - object, - peerId, - pin, - pinManager, - preload, - print, - repo - }) - - return apiManager.update(api, () => { throw new NotStartedError() }).api - } catch (err) { - cancel() - throw err - } - } - -/** - * @param {IPFSRepo} repo - * @param {Object} options - * @param {PrivateKey} options.privateKey - * @param {boolean} [options.emptyRepo] - * @param {number} [options.bits=2048] - Number of bits to use in the generated key - * @param {string[]} options.profiles - * @param {IPFSConfig} options.config - * @param {string} [options.pass] - * @param {(...args:any[]) => void} options.print - * @param {KeyType} [options.algorithm='RSA'] - */ -async function initNewRepo (repo, { privateKey, emptyRepo, algorithm, bits, profiles, config, pass, print }) { - emptyRepo = emptyRepo || false - bits = bits == null ? 2048 : Number(bits) - - config = mergeOptions(applyProfiles(profiles, getDefaultConfig()), config) - - // Verify repo does not exist yet - const exists = await repo.exists() - log('repo exists?', exists) - - if (exists === true) { - throw new Error('repo already exists') - } - - const peerId = await createPeerId({ privateKey, algorithm, bits, print }) - - log('identity generated') - - config.Identity = { - PeerID: peerId.toB58String(), - PrivKey: uint8ArrayToString(peerId.privKey.bytes, 'base64pad') - } - - log('peer identity: %s', config.Identity.PeerID) - - await repo.init(config) - await repo.open() - - log('repo opened') - - // Create libp2p for Keychain creation - const libp2p = Components.libp2p({ - peerId, - repo, - config, - keychainConfig: { - pass - } - }) - - if (libp2p.keychain && libp2p.keychain.opts) { - await libp2p.loadKeychain() - - await repo.config.set('Keychain', { - dek: libp2p.keychain.opts.dek - }) - } - - return { peerId, keychain: libp2p.keychain } -} - -/** - * @param {IPFSRepo} repo - * @param {Object} options - * @param {IPFSConfig} [options.config] - * @param {string[]} [options.profiles] - * @param {string} [options.pass] - */ -async function initExistingRepo (repo, { config: newConfig, profiles, pass }) { - let config = await repo.config.getAll() - - if (newConfig || profiles) { - if (profiles) { - config = applyProfiles(profiles, config) - } - if (newConfig) { - config = mergeOptions(config, newConfig) - } - await repo.config.set(config) - } - - const peerId = await PeerId.createFromPrivKey(config.Identity.PrivKey) - - const libp2p = Components.libp2p({ - peerId, - repo, - config, - keychainConfig: { - pass, - ...config.Keychain - } - }) - - libp2p.keychain && await libp2p.loadKeychain() - - return { peerId, keychain: libp2p.keychain } -} - -/** - * @param {Object} options - * @param {KeyType} [options.algorithm='RSA'] - * @param {PrivateKey} options.privateKey - * @param {number} options.bits - * @param {(...args:any[]) => void} options.print - */ -function createPeerId ({ privateKey, algorithm = 'RSA', bits, print }) { - if (privateKey) { - log('using user-supplied private-key') - return typeof privateKey === 'object' - ? privateKey - : PeerId.createFromPrivKey(uint8ArrayFromString(privateKey, 'base64pad')) - } else { - // Generate peer identity keypair + transform to desired format + add to config. - print('generating %s-bit (rsa only) %s keypair...', bits, algorithm) - // @ts-ignore - expects "Ed25519" | "RSA" | "secp256k1" instoad of string - return PeerId.create({ keyType: algorithm, bits }) - } -} - -async function addEmptyDir ({ dag, pin }) { - const node = new DAGNode(new UnixFs('directory').marshal()) - const cid = await dag.put(node, { - version: 0, - format: multicodec.DAG_PB, - hashAlg: multicodec.SHA2_256, - preload: false - }) - await pin.add(cid) - - return cid -} - -// Apply profiles (e.g. ['server', 'lowpower']) to config -function applyProfiles (profiles, config) { - return (profiles || []).reduce((config, name) => { - const profile = require('./config').profiles[name] - if (!profile) { - throw new Error(`Could not find profile with name '${name}'`) - } - log('applying profile %s', name) - return profile.transform(config) - }, config) -} - -function createApi ({ - add, - addAll, - apiManager, - constructorOptions, - block, - blockService, - dag, - gcLock, - initOptions, - ipld, - keychain, - object, - peerId, - pin, - pinManager, - preload, - print, - repo -}) { - const notStarted = async () => { // eslint-disable-line require-await - throw new NotStartedError() - } - - const resolve = Components.resolve({ ipld }) - const refs = Object.assign(Components.refs({ ipld, resolve, preload }), { - local: Components.refs.local({ repo }) - }) - - const api = { - add, - addAll, - bitswap: { - stat: notStarted, - unwant: notStarted, - wantlist: notStarted, - wantlistForPeer: notStarted - }, - bootstrap: { - add: Components.bootstrap.add({ repo }), - list: Components.bootstrap.list({ repo }), - rm: Components.bootstrap.rm({ repo }) - }, - block, - cat: Components.cat({ ipld, preload }), - config: Components.config({ repo }), - dag, - dns: Components.dns(), - files: Components.files({ ipld, block, blockService, repo, preload, options: constructorOptions }), - get: Components.get({ ipld, preload }), - id: Components.id({ peerId }), - init: async () => { throw new AlreadyInitializedError() }, // eslint-disable-line require-await - isOnline: Components.isOnline({}), - key: { - export: Components.key.export({ keychain }), - gen: Components.key.gen({ keychain }), - import: Components.key.import({ keychain }), - info: Components.key.info({ keychain }), - list: Components.key.list({ keychain }), - rename: Components.key.rename({ keychain }), - rm: Components.key.rm({ keychain }) - }, - ls: Components.ls({ ipld, preload }), - object, - pin, - refs, - repo: { - gc: Components.repo.gc({ gcLock, pin, refs, repo }), - stat: Components.repo.stat({ repo }), - version: Components.repo.version({ repo }) - }, - resolve, - start: Components.start({ - apiManager, - options: constructorOptions, - blockService, - gcLock, - initOptions, - ipld, - keychain, - peerId, - pinManager, - preload, - print, - repo - }), - stats: { - bitswap: notStarted, - bw: notStarted, - repo: Components.repo.stat({ repo }) - }, - stop: () => {}, - swarm: { - addrs: notStarted, - connect: notStarted, - disconnect: notStarted, - localAddrs: Components.swarm.localAddrs({ multiaddrs: [] }), - peers: notStarted - }, - version: Components.version({ repo }) - } - - return api -} - -/** - * @template {boolean | InitOptions} Init - * @template {boolean} Start - * - * @typedef {Object} ConstructorOptions - * Options argument can be used to specify advanced configuration. - * @property {RepoOption} [repo='~/.jsipfs'] - * @property {boolean} [repoAutoMigrate=true] - `js-ipfs` comes bundled with a - * tool that automatically migrates your IPFS repository when a new version is - * available. - * @property {Init} [init=true] - Perform repo initialization steps when creating - * the IPFS node. - * Note that *initializing* a repo is different from creating an instance of - * [`ipfs.Repo`](https://github.com/ipfs/js-ipfs-repo). The IPFS constructor - * sets many special properties when initializing a repo, so you should usually - * not try and call `repoInstance.init()` yourself. - * @property {Start} [start=true] - If `false`, do not automatically - * start the IPFS node. Instead, you’ll need to manually call - * [`node.start()`](https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs/docs/MODULE.md#nodestart) - * yourself. - * @property {string} [pass=null] - A passphrase to encrypt/decrypt your keys. - * @property {boolean} [silent=false] - Prevents all logging output from the - * IPFS node. (Default: `false`) - * @property {RelayOptions} [relay={ enabled: true, hop: { enabled: false, active: false } }] - * - Configure circuit relay (see the [circuit relay tutorial] - * (https://github.com/ipfs/js-ipfs/tree/master/examples/circuit-relaying) - * to learn more). - * @property {boolean} [offline=false] - Run ipfs node offline. The node does - * not connect to the rest of the network but provides a local API. - * @property {PreloadOptions} [preload] - Configure remote preload nodes. - * The remote will preload content added on this node, and also attempt to - * preload objects requested by this node. - * @property {ExperimentalOptions} [EXPERIMENTAL] - Enable and configure - * experimental features. - * @property {object} [config] - Modify the default IPFS node config. This - * object will be *merged* with the default config; it will not replace it. - * (Default: [`config-nodejs.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/config-nodejs.js) - * in Node.js, [`config-browser.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/config-browser.js) - * in browsers) - * @property {import('.').IPLDConfig} [ipld] - Modify the default IPLD config. This object - * will be *merged* with the default config; it will not replace it. Check IPLD - * [docs](https://github.com/ipld/js-ipld#ipld-constructor) for more information - * on the available options. (Default: [`ipld.js`] - * (https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld.js) - * in browsers) - * @property {object|Function} [libp2p] - The libp2p option allows you to build - * your libp2p node by configuration, or via a bundle function. If you are - * looking to just modify the below options, using the object format is the - * quickest way to get the default features of libp2p. If you need to create a - * more customized libp2p node, such as with custom transports or peer/content - * routers that need some of the ipfs data on startup, a custom bundle is a - * great way to achieve this. - * - You can see the bundle in action in the [custom libp2p example](https://github.com/ipfs/js-ipfs/tree/master/examples/custom-libp2p). - * - Please see [libp2p/docs/CONFIGURATION.md](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md) - * for the list of options libp2p supports. - * - Default: [`libp2p-nodejs.js`](../src/core/runtime/libp2p-nodejs.js) - * in Node.js, [`libp2p-browser.js`](../src/core/runtime/libp2p-browser.js) in - * browsers. - * - * @property {boolean} [repoOwner] - */ - -/** - * @typedef {IPFSRepo|string} RepoOption - * The file path at which to store the IPFS node’s data. Alternatively, you - * can set up a customized storage system by providing an `ipfs.Repo` instance. - * - * @example - * ```js - * // Store data outside your user directory - * const node = await IPFS.create({ repo: '/var/ipfs/data' }) - * ``` - * - * @typedef {object} RelayOptions - * @property {boolean} [enabled] - Enable circuit relay dialer and listener. (Default: `true`) - * @property {object} [hop] - * @property {boolean} [hop.enabled] - Make this node a relay (other nodes can connect *through* it). (Default: `false`) - * @property {boolean} [hop.active] - Make this an *active* relay node. Active relay nodes will attempt to dial a destination peer even if that peer is not yet connected to the relay. (Default: `false`) - * - * @typedef {object} PreloadOptions - * @property {boolean} [enabled] - Enable content preloading (Default: `true`) - * @property {number} [interval] - * @property {string[]} [addresses] - Multiaddr API addresses of nodes that should preload content. - * **NOTE:** nodes specified here should also be added to your node's bootstrap address list at `config.Boostrap`. - * - * @typedef {object} ExperimentalOptions - * @property {boolean} [ipnsPubsub] - Enable pub-sub on IPNS. (Default: `false`) - * @property {boolean} [sharding] - Enable directory sharding. Directories that have many child objects will be represented by multiple DAG nodes instead of just one. It can improve lookup performance when a directory has several thousand files or more. (Default: `false`) - * - * @typedef {Object} InitOptions - * @property {boolean} [emptyRepo=false] - Whether to remove built-in assets, - * like the instructional tour and empty mutable file system, from the repo. - * @property {number} [bits=2048] - Number of bits to use in the generated key - * pair (rsa only). - * @property {PrivateKey} [privateKey] - A pre-generated private key to use. - * **NOTE: This overrides `bits`.** - * @property {string} [pass] - A passphrase to encrypt keys. You should - * generally use the top-level `pass` option instead of the `init.pass` - * option (this one will take its value from the top-level option if not set). - * @property {string[]} [profiles] - Apply profile settings to config. - * @property {boolean} [allowNew=true] - Set to `false` to disallow - * initialization if the repo does not already exist. - * @property {IPFSConfig} [config] - * - * @typedef {import('./config').IPFSConfig} IPFSConfig - * @typedef {import('.').IPFSRepo} IPFSRepo - * - * @typedef {'RSA' | 'ed25519' | 'secp256k1'} KeyType - * - * @typedef {string|PeerId} PrivateKey - * Can be either a base64 string or a [PeerId](https://github.com/libp2p/js-peer-id) - * instance. - * - * @typedef {import('libp2p').Keychain} Keychain - */ diff --git a/packages/ipfs-core/src/components/ipld.js b/packages/ipfs-core/src/components/ipld.js new file mode 100644 index 0000000000..a8b465bc08 --- /dev/null +++ b/packages/ipfs-core/src/components/ipld.js @@ -0,0 +1,23 @@ +'use strict' + +const getDefaultIpldOptions = require('../runtime/ipld') +const Ipld = require('ipld') + +/** + * @param {Object} config + * @param {BlockService} config.blockService + * @param {Print} config.print + * @param {Options} [config.options] + * @returns {IPLD} + */ +const createIPLD = ({ blockService, print, options }) => + new Ipld(getDefaultIpldOptions(blockService, options, print)) +module.exports = createIPLD + +/** + * @typedef {import('../interface/ipld').IPLD} IPLD + * @typedef {import('../interface/ipld').Options} Options + * @typedef {import('../interface/block-service').BlockService} BlockService + * @typedef {import('../interface/basic').Block} Block + * @typedef {import('.').Print} Print + */ diff --git a/packages/ipfs-core/src/components/ipns.js b/packages/ipfs-core/src/components/ipns.js new file mode 100644 index 0000000000..0f26670896 --- /dev/null +++ b/packages/ipfs-core/src/components/ipns.js @@ -0,0 +1,110 @@ +'use strict' + +const IPNS = require('../ipns') +const routingConfig = require('../ipns/routing/config') +const OfflineDatastore = require('../ipns/routing/offline-datastore') +const { NotInitializedError, AlreadyInitializedError } = require('../errors') +const log = require('debug')('ipfs:components:ipns') + +class IPNSAPI { + /** + * @param {Object} [options] + * @param {string} [options.pass] + * @param {boolean} [options.offline] + * @param {LibP2POptions} [options.libp2p] + * @param {ExperimentalOptions} [options.EXPERIMENTAL] + */ + constructor (options = {}) { + this.options = options + this.offline = null + this.online = null + } + + getIPNS () { + const ipns = this.online || this.offline + if (ipns) { + return ipns + } else { + throw new NotInitializedError() + } + } + + get routing () { + return this.getIPNS().routing + } + + /** + * Activates IPNS subsystem in an ofline mode. If it was started once already + * it will throw an exception. + * + * This is primarily used for offline ipns modifications, such as the + * initializeKeyspace feature. + * + * @param {Object} config + * @param {import('.').Repo} config.repo + * @param {import('.').PeerId} config.peerId + * @param {import('.').Keychain} config.keychain + */ + startOffline ({ repo, peerId, keychain }) { + if (this.offline != null) { + throw new AlreadyInitializedError() + } + + log('initializing IPNS keyspace') + + const routing = new OfflineDatastore(repo) + const ipns = new IPNS(routing, repo.datastore, peerId, keychain, this.options) + + this.offline = ipns + } + + /** + * @param {Object} config + * @param {import('.').LibP2P} config.libp2p + * @param {import('.').Repo} config.repo + * @param {import('.').PeerId} config.peerId + * @param {import('.').Keychain} config.keychain + */ + startOnline ({ libp2p, repo, peerId, keychain }) { + if (this.online != null) { + throw new AlreadyInitializedError() + } + + const routing = routingConfig({ libp2p, repo, peerId, options: this.options }) + const ipns = new IPNS(routing, repo.datastore, peerId, keychain, this.options) + this.online = ipns + } + + async stop () { + const ipns = this.online + if (ipns) { + await ipns.republisher.stop() + } + } + + publish (privKey, value, lifetime) { + return this.getIPNS().publish(privKey, value, lifetime) + } + + resolve (name, options) { + return this.getIPNS().resolve(name, options) + } + + initializeKeyspace (privKey, value) { + return this.getIPNS().initializeKeyspace(privKey, value) + } +} +module.exports = IPNSAPI + +/** + * @typedef {Object} ExperimentalOptions + * @property {boolean} [ipnsPubsub] + * + * @typedef {Object} LibP2POptions + * @property {DHTConfig} [config] + * + * @typedef {Object} DHTConfig + * @property {boolean} [enabled] + * + * @typedef {import('../ipns')} IPNS + */ diff --git a/packages/ipfs-core/src/components/is-online.js b/packages/ipfs-core/src/components/is-online.js index 450b62e153..1e9593a0f8 100644 --- a/packages/ipfs-core/src/components/is-online.js +++ b/packages/ipfs-core/src/components/is-online.js @@ -2,7 +2,13 @@ /** * @param {Object} config - * @param {import('libp2p')} [config.libp2p] + * @param {import('.').NetworkService} config.network */ -module.exports = ({ libp2p }) => () => - Boolean(libp2p && libp2p.isStarted()) +module.exports = ({ network }) => + /** + * @returns {boolean} + */ + () => { + const net = network.try() + return net != null && net.libp2p.isStarted() + } diff --git a/packages/ipfs-core/src/components/key/export.js b/packages/ipfs-core/src/components/key/export.js index 0e136c7f70..056ba49c15 100644 --- a/packages/ipfs-core/src/components/key/export.js +++ b/packages/ipfs-core/src/components/key/export.js @@ -2,6 +2,33 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption((name, password, options = {}) => keychain.exportKey(name, password, options)) + /** + * Remove a key + * + * @example + * ```js + * const pem = await ipfs.key.export('self', 'password') + * + * console.log(pem) + * // -----BEGIN ENCRYPTED PRIVATE KEY----- + * // MIIFDTA/BgkqhkiG9w0BBQ0wMjAaBgkqhkiG9w0BBQwwDQQIpdO40RVyBwACAWQw + * // ... + * // YA== + * // -----END ENCRYPTED PRIVATE KEY----- + * ``` + * @param {string} name - The name of the key to export + * @param {string} password - Password to set on the PEM output + * @param {import('.').AbortOptions} options + * @returns {Promise} - The string representation of the key + */ + const exportKey = async (name, password, options) => { + return await keychain.exportKey(name, password, options) + } + + return withTimeoutOption(exportKey) } diff --git a/packages/ipfs-core/src/components/key/gen.js b/packages/ipfs-core/src/components/key/gen.js index e3eaea5ff0..e8d679d7f7 100644 --- a/packages/ipfs-core/src/components/key/gen.js +++ b/packages/ipfs-core/src/components/key/gen.js @@ -2,8 +2,45 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption((name, options = {}) => { - return keychain.createKey(name, options.type || 'rsa', options.size || 2048) - }) + /** + * Generate a new key + * + * @example + * ```js + * const key = await ipfs.key.gen('my-key', { + * type: 'rsa', + * size: 2048 + * }) + * + * console.log(key) + * // { id: 'QmYWqAFvLWb2G5A69JGXui2JJXzaHXiUEmQkQgor6kNNcJ', + * // name: 'my-key' } + * ``` + * + * @param {string} name - The name to give the key + * @param {GenOptions & AbortOptions} options + * @returns {Promise<>} + */ + const gen = async (name, options = {}) => { + return await keychain.createKey(name, options.type || 'rsa', options.size || 2048) + } + + return withTimeoutOption(gen) } + +/** + * @typedef {Object} GenOptions + * @property {import('libp2p-crypto').KeyType} [type='RSA'] - The key type + * @property {number} [size=2048] - The key size in bits + * + * @typedef {Object} Key + * @property {string} id + * @property {string} name + * + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/key/import.js b/packages/ipfs-core/src/components/key/import.js index 1aaba9559f..37b439d798 100644 --- a/packages/ipfs-core/src/components/key/import.js +++ b/packages/ipfs-core/src/components/key/import.js @@ -2,6 +2,37 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption((name, pem, password, options) => keychain.importKey(name, pem, password, options)) + /** + * Remove a key + * + * @example + * ```js + * const key = await ipfs.key.import('clone', pem, 'password') + * + * console.log(key) + * // { id: 'QmQRiays958UM7norGRQUG3tmrLq8pJdmJarwYSk2eLthQ', + * // name: 'clone' } + * ``` + * @param {string} name - The name of the key to import + * @param {string} pem - The PEM encoded key + * @param {string} password - The password that protects the PEM key + * @param {import('.').AbortOptions} options + * @returns {Promise} - An object that describes the new key + */ + const importKey = async (name, pem, password, options) => { + return await keychain.importKey(name, pem, password, options) + } + + return withTimeoutOption(importKey) } + +/** + * @typedef {Object} ImportedKey + * @property {string} id + * @property {string} name + */ diff --git a/packages/ipfs-core/src/components/key/index.js b/packages/ipfs-core/src/components/key/index.js new file mode 100644 index 0000000000..586cdfd720 --- /dev/null +++ b/packages/ipfs-core/src/components/key/index.js @@ -0,0 +1,31 @@ +'use strict' + +const createExport = require('./export') +const createGen = require('./gen') +const createImport = require('./import') +const createInfo = require('./info') +const createList = require('./list') +const createRename = require('./rename') +const createRm = require('./rm') + +class KeyAPI { + /** + * @param {Object} config + * @param {Keychain} config.keychain + */ + constructor ({ keychain }) { + this.gen = createGen({ keychain }) + this.list = createList({ keychain }) + this.rm = createRm({ keychain }) + this.rename = createRename({ keychain }) + this.export = createExport({ keychain }) + this.import = createImport({ keychain }) + this.info = createInfo({ keychain }) + } +} +module.exports = KeyAPI + +/** + * @typedef {import('..').Keychain} Keychain + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/key/info.js b/packages/ipfs-core/src/components/key/info.js index 209ee27eba..4f396e443c 100644 --- a/packages/ipfs-core/src/components/key/info.js +++ b/packages/ipfs-core/src/components/key/info.js @@ -2,6 +2,24 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption((name, options) => keychain.findKeyByName(name, options)) + /** + * @param {string} name + * @param {AbortOptions} [options] + * @returns {Promise} + */ + const info = async (name, options = {}) => { + return await keychain.findKeyByName(name, options) + } + + return withTimeoutOption(info) } + +/** + * @typedef {import('./gen').Key} Key + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/key/list.js b/packages/ipfs-core/src/components/key/list.js index 576132f7b6..2fcda87a30 100644 --- a/packages/ipfs-core/src/components/key/list.js +++ b/packages/ipfs-core/src/components/key/list.js @@ -2,6 +2,41 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption((options) => keychain.listKeys(options)) + /** + * List all the keys + * + * @example + * ```js + * const keys = await ipfs.key.list() + * + * console.log(keys) + * // [ + * // { id: 'QmTe4tuceM2sAmuZiFsJ9tmAopA8au71NabBDdpPYDjxAb', + * // name: 'self' }, + * // { id: 'QmWETF5QvzGnP7jKq5sPDiRjSM2fzwzNsna4wSBEzRzK6W', + * // name: 'my-key' } + * // ] + * ``` + * + * @param {AbortOptions} [options] + * @returns {Promise} + */ + const list = async (options = {}) => { + return await keychain.listKeys(options) + } + + return withTimeoutOption(list) } + +/** + * @typedef {Object} KeyEntry + * @property {string} name - The name of the key + * @property {string} hash - The hash of the key + * + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/key/rename.js b/packages/ipfs-core/src/components/key/rename.js index acd6a0c72f..7b4da5bedb 100644 --- a/packages/ipfs-core/src/components/key/rename.js +++ b/packages/ipfs-core/src/components/key/rename.js @@ -2,8 +2,30 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption(async (oldName, newName, options) => { + /** + * Rename a key + * + * @example + * ```js + * const key = await ipfs.key.rename('my-key', 'my-new-key') + * + * console.log(key) + * // { id: 'Qmd4xC46Um6s24MradViGLFtMitvrR4SVexKUgPgFjMNzg', + * // was: 'my-key', + * // now: 'my-new-key', + * // overwrite: false } + * ``` + * @param {string} oldName - The current key name + * @param {string} newName - The desired key name + * @param {AbortOptions} [options] + * @returns {Promise} + */ + const rename = async (oldName, newName, options = {}) => { const key = await keychain.renameKey(oldName, newName, options) return { was: oldName, @@ -11,5 +33,17 @@ module.exports = ({ keychain }) => { id: key.id, overwrite: false } - }) + } + + return withTimeoutOption(rename) } + +/** + * @typedef {Object} RenamedKey + * @property {string} was - The name of the key + * @property {string} now - The hash of the key + * @property {string} id + * @property {boolean} overwrite + * + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/key/rm.js b/packages/ipfs-core/src/components/key/rm.js index bd24943021..1b91de0466 100644 --- a/packages/ipfs-core/src/components/key/rm.js +++ b/packages/ipfs-core/src/components/key/rm.js @@ -2,6 +2,36 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Keychain} config.keychain + */ module.exports = ({ keychain }) => { - return withTimeoutOption((name, options) => keychain.removeKey(name, options)) + /** + * Remove a key + * + * @example + * ```js + * const key = await ipfs.key.rm('my-key') + * + * console.log(key) + * // { id: 'QmWETF5QvzGnP7jKq5sPDiRjSM2fzwzNsna4wSBEzRzK6W', + * // name: 'my-key' } + * ``` + * + * @param {string} name - The name of the key to remove + * @param {import('.').AbortOptions} options + * @returns {Promise} - An object that describes the removed key + */ + const rm = async (name, options) => { + return await keychain.removeKey(name, options) + } + + return withTimeoutOption(rm) } + +/** + * @typedef {Object} RemovedKey + * @property {string} name - The name of the key + * @property {string} id - The hash of the key + */ diff --git a/packages/ipfs-core/src/components/libp2p.js b/packages/ipfs-core/src/components/libp2p.js index 32faca1dd2..a181bc3924 100644 --- a/packages/ipfs-core/src/components/libp2p.js +++ b/packages/ipfs-core/src/components/libp2p.js @@ -7,13 +7,13 @@ const PubsubRouters = require('../runtime/libp2p-pubsub-routers-nodejs') /** * @param {Object} config - * @param {import('.').IPFSRepo} config.repo - * @param {Object} [config.options] - * @param {import('.').PeerId} [config.peerId] - * @param {string[]} [config.multiaddrs] + * @param {Repo} config.repo + * @param {Options} [config.options] + * @param {PeerId} [config.peerId] + * @param {string[]|Multiaddr[]} [config.multiaddrs] * @param {{pass?:string}} [config.keychainConfig] - * @param {import('.').LibP2PConfig} [config.config] - * @returns {import('.').LibP2PService} + * @param {Config} [config.config] + * @returns {LibP2P} */ module.exports = ({ options = {}, @@ -141,3 +141,12 @@ function getLibp2pOptions ({ options, config, datastore, keys, keychainConfig, p return libp2pConfig } + +/** + * @typedef {import('.').Repo} Repo + * @typedef {import('.').Multiaddr} Multiaddr + * @typedef {import('.').PeerId} PeerId + * @typedef {import('libp2p')} LibP2P + * @typedef {import('libp2p').Options} Options + * @typedef {import('libp2p').Config} Config + */ diff --git a/packages/ipfs-core/src/components/ls.js b/packages/ipfs-core/src/components/ls.js index e1cb13b2bd..88b2866993 100644 --- a/packages/ipfs-core/src/components/ls.js +++ b/packages/ipfs-core/src/components/ls.js @@ -6,8 +6,8 @@ const { normalizeCidPath, mapFile, withTimeoutOption } = require('../utils') /** * @param {Object} config - * @param {import('.').IPLD} config.ipld - * @param {import('.').Preload} config.preload + * @param {import('./core').IPLD} config.ipld + * @param {import('./core').Preload} config.preload */ module.exports = function ({ ipld, preload }) { /** diff --git a/packages/ipfs-core/src/components/name/index.js b/packages/ipfs-core/src/components/name/index.js new file mode 100644 index 0000000000..a8bb3b7faf --- /dev/null +++ b/packages/ipfs-core/src/components/name/index.js @@ -0,0 +1,41 @@ +'use strict' + +const createPublishAPI = require('./publish') +const createResolveAPI = require('./resolve') +const PubSubAPI = require('./pubsub') +class NameAPI { + /** + * @param {Object} config + * @param {IPNS} config.ipns + * @param {PeerId} config.peerId + * @param {Options} config.options + * @param {DagReader} config.dagReader + * @param {IsOnline} config.isOnline + * @param {Keychain} config.keychain + * @param {DNS} config.dns + */ + constructor ({ dns, ipns, dagReader, peerId, isOnline, keychain, options }) { + this.publish = createPublishAPI({ ipns, dagReader, peerId, isOnline, keychain }) + this.resolve = createResolveAPI({ dns, ipns, peerId, isOnline, options }) + this.pubsub = new PubSubAPI({ ipns, options: options.EXPERIMENTAL }) + } +} +module.exports = NameAPI + +/** + * @typedef {ResolveOptions & ExperimentalOptions} Options + * + * @typedef {Object} ExperimentalOptions + * @property {PubSubOptions} [EXPERIMENTAL] + * + * @typedef {import('./pubsub').Options} PubSubOptions + * @typedef {import('./resolve').ResolveOptions} ResolveOptions + * + * @typedef {import('..').IPNS} IPNS + * @typedef {import('..').PeerId} PeerId + * @typedef {import('..').DagReader} DagReader + * @typedef {import('..').Keychain} Keychain + * @typedef {import('..').IsOnline} IsOnline + * @typedef {import('..').DNS} DNS + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/name/publish.js b/packages/ipfs-core/src/components/name/publish.js index 217d2e41ca..8e2415e644 100644 --- a/packages/ipfs-core/src/components/name/publish.js +++ b/packages/ipfs-core/src/components/name/publish.js @@ -16,13 +16,13 @@ const { resolvePath } = require('./utils') * IPNS - Inter-Planetary Naming System * * @param {Object} config - * @param {import('../../ipns')} config.ipns - * @param {import('../index').DAG} config.dag - * @param {import('peer-id')} config.peerId - * @param {import('../index').IsOnline} config.isOnline - * @param {import('../init').Keychain} config.keychain + * @param {import('.').IPNS} config.ipns + * @param {import('.').DagReader} config.dagReader + * @param {import('.').PeerId} config.peerId + * @param {import('.').IsOnline} config.isOnline + * @param {import('.').Keychain} config.keychain */ -module.exports = ({ ipns, dag, peerId, isOnline, keychain }) => { +module.exports = ({ ipns, dagReader, peerId, isOnline, keychain }) => { const lookupKey = async keyName => { if (keyName === 'self') { return peerId.privKey @@ -93,7 +93,7 @@ module.exports = ({ ipns, dag, peerId, isOnline, keychain }) => { const results = await Promise.all([ // verify if the path exists, if not, an error will stop the execution lookupKey(key), - resolve ? resolvePath({ ipns, dag }, value) : Promise.resolve() + resolve ? resolvePath({ ipns, dagReader }, value) : Promise.resolve() ]) // Start publishing process @@ -123,5 +123,5 @@ module.exports = ({ ipns, dag, peerId, isOnline, keychain }) => { * @property {string} name * @property {string} value * - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/name/pubsub/cancel.js b/packages/ipfs-core/src/components/name/pubsub/cancel.js index 1eb1b50985..288a0ec012 100644 --- a/packages/ipfs-core/src/components/name/pubsub/cancel.js +++ b/packages/ipfs-core/src/components/name/pubsub/cancel.js @@ -5,15 +5,15 @@ const { withTimeoutOption } = require('../../../utils') /** * @param {Object} config - * @param {import('../../../ipns')} config.ipns - * @param {import('../../init').ConstructorOptions} config.options + * @param {import('.').IPNS} config.ipns + * @param {import('.').Options} [config.options] */ -module.exports = ({ ipns, options: constructorOptions }) => { +module.exports = ({ ipns, options: routingOptions }) => { /** * Cancel a name subscription. * * @param {string} name - The name of the subscription to cancel. - * @param {AbortOptions} [options] + * @param {import('.').AbortOptions} [options] * @returns {Promise<{ canceled: boolean }>} * @example * ```js @@ -24,13 +24,9 @@ module.exports = ({ ipns, options: constructorOptions }) => { * ``` */ async function cancel (name, options) { // eslint-disable-line require-await - const pubsub = getPubsubRouting(ipns, constructorOptions) + const pubsub = getPubsubRouting(ipns, routingOptions) return pubsub.cancel(name, options) } return withTimeoutOption(cancel) } - -/** - * @typedef {import('../../../utils').AbortOptions} AbortOptions - */ diff --git a/packages/ipfs-core/src/components/name/pubsub/index.js b/packages/ipfs-core/src/components/name/pubsub/index.js new file mode 100644 index 0000000000..1ea174e082 --- /dev/null +++ b/packages/ipfs-core/src/components/name/pubsub/index.js @@ -0,0 +1,25 @@ +'use strict' + +const createCancelAPI = require('./cancel') +const createStateAPI = require('./state') +const createSubsAPI = require('./subs') + +class PubSubAPI { + /** + * @param {Object} config + * @param {IPNS} config.ipns + * @param {Options} [config.options] + */ + constructor ({ ipns, options }) { + this.cancel = createCancelAPI({ ipns, options }) + this.state = createStateAPI({ ipns, options }) + this.subs = createSubsAPI({ ipns, options }) + } +} +module.exports = PubSubAPI + +/** + * @typedef {import('..').IPNS} IPNS + * @typedef {import('./utils').PubSubRoutingOptions} Options + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/name/pubsub/state.js b/packages/ipfs-core/src/components/name/pubsub/state.js index f122da1396..3448f183b8 100644 --- a/packages/ipfs-core/src/components/name/pubsub/state.js +++ b/packages/ipfs-core/src/components/name/pubsub/state.js @@ -2,12 +2,16 @@ const { getPubsubRouting } = require('./utils') const { withTimeoutOption } = require('../../../utils') - -module.exports = ({ ipns, options: constructorOptions }) => { +/** + * @param {Object} config + * @param {import('.').IPNS} config.ipns + * @param {import('.').Options} [config.options] + */ +module.exports = ({ ipns, options: routingOptions }) => { /** * Query the state of IPNS pubsub. * - * @param {AbortOptions} [_options] + * @param {import('.').AbortOptions} [_options] * @returns {Promise<{ enabled: boolean }>} * ```js * const result = await ipfs.name.pubsub.state() @@ -17,7 +21,7 @@ module.exports = ({ ipns, options: constructorOptions }) => { */ async function state (_options) { // eslint-disable-line require-await try { - return { enabled: Boolean(getPubsubRouting(ipns, constructorOptions)) } + return { enabled: Boolean(getPubsubRouting(ipns, routingOptions)) } } catch (err) { return { enabled: false } } @@ -25,7 +29,3 @@ module.exports = ({ ipns, options: constructorOptions }) => { return withTimeoutOption(state) } - -/** - * @typedef {import('../../../utils').AbortOptions} AbortOptions - */ diff --git a/packages/ipfs-core/src/components/name/pubsub/subs.js b/packages/ipfs-core/src/components/name/pubsub/subs.js index 6e84f8ec3c..0a5543f8ca 100644 --- a/packages/ipfs-core/src/components/name/pubsub/subs.js +++ b/packages/ipfs-core/src/components/name/pubsub/subs.js @@ -3,11 +3,16 @@ const { getPubsubRouting } = require('./utils') const { withTimeoutOption } = require('../../../utils') -module.exports = ({ ipns, options: constructorOptions }) => { +/** + * @param {Object} config + * @param {import('.').IPNS} config.ipns + * @param {import('.').Options} [config.options] + */ +module.exports = ({ ipns, options: routingOptions }) => { /** * Show current name subscriptions. * - * @param {AbortOptions} [options] + * @param {import('.').AbortOptions} [options] * @returns {Promise} * @example * ```js @@ -17,13 +22,9 @@ module.exports = ({ ipns, options: constructorOptions }) => { * ``` */ async function subs (options) { // eslint-disable-line require-await - const pubsub = getPubsubRouting(ipns, constructorOptions) + const pubsub = getPubsubRouting(ipns, routingOptions) return pubsub.getSubscriptions(options) } return withTimeoutOption(subs) } - -/** - * @typedef {import('../../../utils').AbortOptions} AbortOptions - */ diff --git a/packages/ipfs-core/src/components/name/pubsub/utils.js b/packages/ipfs-core/src/components/name/pubsub/utils.js index ee53a96f9c..43e7e69ae9 100644 --- a/packages/ipfs-core/src/components/name/pubsub/utils.js +++ b/packages/ipfs-core/src/components/name/pubsub/utils.js @@ -3,9 +3,14 @@ const IpnsPubsubDatastore = require('../../../ipns/routing/pubsub-datastore') const errcode = require('err-code') -// Get pubsub from IPNS routing +/** + * Get pubsub from IPNS routing + * + * @param {import('.').IPNS} ipns + * @param {PubSubRoutingOptions} [options] + */ exports.getPubsubRouting = (ipns, options) => { - if (!ipns || !(options.EXPERIMENTAL && options.EXPERIMENTAL.ipnsPubsub)) { + if (!ipns || !(options && options.ipnsPubsub)) { throw errcode(new Error('IPNS pubsub subsystem is not enabled'), 'ERR_IPNS_PUBSUB_NOT_ENABLED') } @@ -23,3 +28,8 @@ exports.getPubsubRouting = (ipns, options) => { return pubsub } + +/** + * @typedef {Object} PubSubRoutingOptions + * @property {boolean} [ipnsPubsub] - Enable pub-sub on IPNS. (Default: `false`) + */ diff --git a/packages/ipfs-core/src/components/name/resolve.js b/packages/ipfs-core/src/components/name/resolve.js index ad3eb704f5..59f3f03de5 100644 --- a/packages/ipfs-core/src/components/name/resolve.js +++ b/packages/ipfs-core/src/components/name/resolve.js @@ -2,8 +2,7 @@ const debug = require('debug') const errcode = require('err-code') -/** @type {typeof Object.assign} */ -const mergeOptions = require('merge-options') +const { mergeOptions } = require('../../utils') const CID = require('cids') const isDomain = require('is-domain-name') @@ -28,18 +27,18 @@ const appendRemainder = (result, remainder) => * IPNS - Inter-Planetary Naming System * * @param {Object} config - * @param {import('../index').DNS} config.dns - * @param {import('../../ipns')} config.ipns - * @param {import('peer-id')} config.peerId - * @param {import('../index').IsOnline} config.isOnline - * @param {{offline?:boolean}} config.options + * @param {import('.').DNS} config.dns + * @param {import('.').IPNS} config.ipns + * @param {import('.').PeerId} config.peerId + * @param {import('.').IsOnline} config.isOnline + * @param {ResolveOptions} config.options */ -module.exports = ({ dns, ipns, peerId, isOnline, options: constructorOptions }) => { +module.exports = ({ dns, ipns, peerId, isOnline, options: { offline } }) => { /** * Given a key, query the DHT for its best value. * * @param {string} name - ipns name to resolve. Defaults to your node's peerID. - * @param {ResolveOptions} [options] + * @param {Options & AbortOptions} [options] * @returns {AsyncIterable} * @example * ```js @@ -58,8 +57,6 @@ module.exports = ({ dns, ipns, peerId, isOnline, options: constructorOptions }) recursive: true }, options) - const { offline } = constructorOptions - // TODO: params related logic should be in the core implementation if (offline && options.nocache) { throw errcode(new Error('cannot specify both offline and nocache'), 'ERR_NOCACHE_AND_OFFLINE') @@ -104,11 +101,12 @@ module.exports = ({ dns, ipns, peerId, isOnline, options: constructorOptions }) /** * IPFS resolve options. * - * @typedef {ResolveSettings & AbortOptions} ResolveOptions - * - * @typedef {Object} ResolveSettings + * @typedef {Object} Options * @property {boolean} [options.nocache=false] - do not use cached entries. * @property {boolean} [options.recursive=true] - resolve until the result is not an IPNS name. * - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {Object} ResolveOptions + * @property {boolean} [offline] + * + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/name/utils.js b/packages/ipfs-core/src/components/name/utils.js index acfb307fbd..b2b5970f78 100644 --- a/packages/ipfs-core/src/components/name/utils.js +++ b/packages/ipfs-core/src/components/name/utils.js @@ -2,14 +2,26 @@ const isIPFS = require('is-ipfs') -// resolves the given path by parsing out protocol-specific entries -// (e.g. /ipns/) and then going through the /ipfs/ entries and returning the final node -exports.resolvePath = ({ ipns, dag }, name) => { +/** + * resolves the given path by parsing out protocol-specific entries + * (e.g. /ipns/) and then going through the /ipfs/ entries and returning the final node + * + * @param {Object} context + * @param {IPNS} context.ipns + * @param {DagReader} context.dagReader + * @param {string} name + */ +exports.resolvePath = ({ ipns, dagReader }, name) => { // ipns path if (isIPFS.ipnsPath(name)) { return ipns.resolve(name) } // ipfs path - return dag.get(name.substring('/ipfs/'.length)) + return dagReader.get(name.substring('/ipfs/'.length)) } + +/** + * @typedef {import('.').DagReader} DagReader + * @typedef {import('.').IPNS} IPNS + */ diff --git a/packages/ipfs-core/src/components/network.js b/packages/ipfs-core/src/components/network.js new file mode 100644 index 0000000000..d8b7575aac --- /dev/null +++ b/packages/ipfs-core/src/components/network.js @@ -0,0 +1,129 @@ +'use strict' + +const IPFSBitswap = require('ipfs-bitswap') +const createLibP2P = require('./libp2p') +const Multiaddr = require('multiaddr') +const errCode = require('err-code') + +class Network { + /** + * @param {PeerId} peerId + * @param {LibP2P} libp2p + * @param {BitSwap} bitswap + */ + constructor (peerId, libp2p, bitswap) { + this.peerId = peerId + this.libp2p = libp2p + this.bitswap = bitswap + } + + /** + * @param {Options} options + */ + static async start ({ peerId, repo, print, pass }) { + // Need to ensure that repo is open as it could have been closed between + // `init` and `start`. + if (repo.closed) { + await repo.open() + } + + const config = await repo.config.getAll() + + const libp2p = createLibP2P({ + repo, + peerId, + multiaddrs: readAddrs(peerId, config), + config, + keychainConfig: { + pass, + ...config.Keychain + } + }) + + if (libp2p.keychain) { + await libp2p.loadKeychain() + + // If it is a new repo we don't have Keychain persisted yet. + if (libp2p.keychain.opts && !config.Keychain) { + await repo.config.set('Keychain', { + dek: libp2p.keychain.opts.dek + }) + } + } + + await libp2p.start() + + for (const ma of libp2p.transportManager.getAddrs()) { + print(`Swarm listening on ${ma}/p2p/${peerId.toB58String()}`) + } + + const bitswap = new IPFSBitswap(libp2p, repo.blocks, { statsEnabled: true }) + await bitswap.start() + + return new Network(peerId, libp2p, bitswap) + } + + /** + * @param {Network} network + */ + // eslint-disable-next-line require-await + static async stop (network) { + network.bitswap.stop() + } +} +module.exports = Network + +/** + * + * @param {PeerId} peerId + * @param {IPFSConfig} config + * @returns {Multiaddr[]} + */ +const readAddrs = (peerId, config) => { + const peerIdStr = peerId.toB58String() + /** @type {Multiaddr[]} */ + const addrs = [] + const swarm = (config.Addresses && config.Addresses.Swarm) || [] + for (const addr of swarm) { + let ma = Multiaddr(addr) + + // Temporary error for users migrating using websocket-star multiaddrs for listenning on libp2p + // websocket-star support was removed from ipfs and libp2p + if (ma.protoCodes().includes(WEBSOCKET_STAR_PROTO_CODE)) { + throw errCode(new Error('websocket-star swarm addresses are not supported. See https://github.com/ipfs/js-ipfs/issues/2779'), 'ERR_WEBSOCKET_STAR_SWARM_ADDR_NOT_SUPPORTED') + } + + // multiaddrs that go via a signalling server or other intermediary (e.g. stardust, + // webrtc-star) can have the intermediary's peer ID in the address, so append our + // peer ID to the end of it + const maId = ma.getPeerId() + if (maId && maId !== peerIdStr) { + ma = ma.encapsulate(`/p2p/${peerIdStr}`) + } + + addrs.push(ma) + } + + return addrs +} + +const WEBSOCKET_STAR_PROTO_CODE = 479 +/** + * @typedef {Object} Online + * @property {LibP2P} libp2p + * @property {BitSwap} bitswap + * + * @typedef {Object} Options + * @property {PeerId} options.peerId + * @property {Repo} options.repo + * @property {Print} options.print + * @property {string} [options.pass] + * + * @typedef {import('.').IPFSConfig} IPFSConfig + * @typedef {import('.').Repo} Repo + * @typedef {import('.').Print} Print + * @typedef {import('.').LibP2P} LibP2P + * @typedef {import('../interface/bitswap').Bitswap} BitSwap + * @typedef {import('.').PeerId} PeerId + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/data.js b/packages/ipfs-core/src/components/object/data.js index 7143daf308..a41d13ecc8 100644 --- a/packages/ipfs-core/src/components/object/data.js +++ b/packages/ipfs-core/src/components/object/data.js @@ -2,10 +2,29 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + */ module.exports = ({ ipld, preload }) => { const get = require('./get')({ ipld, preload }) - return withTimeoutOption(async function data (multihash, options) { + + /** + * @param {import('.').CID} multihash + * @param {GetOptions & AbortOptions} options + * @returns {Promise} + */ + async function data (multihash, options) { const node = await get(multihash, options) return node.Data - }) + } + + return withTimeoutOption(data) } + +/** + * @typedef {import('cids')} CID + * @typedef {import('./get').GetOptions} GetOptions + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/get.js b/packages/ipfs-core/src/components/object/get.js index 70b8d6785c..dc45835349 100644 --- a/packages/ipfs-core/src/components/object/get.js +++ b/packages/ipfs-core/src/components/object/get.js @@ -5,6 +5,11 @@ const errCode = require('err-code') const { withTimeoutOption } = require('../../utils') const uint8ArrayFromString = require('uint8arrays/from-string') +/** + * @param {string|Uint8Array|CID} multihash + * @param {string} [enc] + * @returns {string|Uint8Array} + */ function normalizeMultihash (multihash, enc) { if (typeof multihash === 'string') { if (enc === 'base58' || !enc) { @@ -19,8 +24,18 @@ function normalizeMultihash (multihash, enc) { throw new Error('unsupported multihash') } +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + */ module.exports = ({ ipld, preload }) => { - return withTimeoutOption(async function get (multihash, options = {}) { // eslint-disable-line require-await + /** + * + * @param {CID} multihash + * @param {GetOptions & AbortOptions} [options] + */ + async function get (multihash, options = {}) { // eslint-disable-line require-await let mh, cid try { @@ -44,5 +59,16 @@ module.exports = ({ ipld, preload }) => { } return ipld.get(cid, { signal: options.signal }) - }) + } + + return withTimeoutOption(get) } + +/** + * @typedef {Object} GetOptions + * @property {boolean} [preload] + * @property {number} [cidVersion] + * @property {string} [enc] + * + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/index.js b/packages/ipfs-core/src/components/object/index.js new file mode 100644 index 0000000000..210ab36f3d --- /dev/null +++ b/packages/ipfs-core/src/components/object/index.js @@ -0,0 +1,39 @@ +'use strict' + +const createData = require('./data') +const createGet = require('./get') +const createLinks = require('./links') +const createNew = require('./new') +const createPut = require('./put') +const createStat = require('./stat') +const ObjectPatchAPI = require('./patch') + +class ObjectAPI { + /** + * @param {Object} config + * @param {IPLD} config.ipld + * @param {Preload} config.preload + * @param {GCLock} config.gcLock + * @param {Dag} config.dag + */ + constructor ({ ipld, preload, dag, gcLock }) { + this.data = createData({ ipld, preload }) + this.get = createGet({ ipld, preload }) + this.links = createLinks({ dag }) + this.new = createNew({ ipld, preload }) + this.put = createPut({ ipld, preload, gcLock }) + this.stat = createStat({ ipld, preload }) + this.patch = new ObjectPatchAPI({ ipld, preload, gcLock }) + } +} + +module.exports = ObjectAPI + +/** + * @typedef {import('..').IPLD} IPLD + * @typedef {import('..').Preload} Preload + * @typedef {import('..').GCLock} GCLock + * @typedef {import('..').Dag} Dag + * @typedef {import('..').CID} CID + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/links.js b/packages/ipfs-core/src/components/object/links.js index 8d9afb1929..6e3eef5548 100644 --- a/packages/ipfs-core/src/components/object/links.js +++ b/packages/ipfs-core/src/components/object/links.js @@ -5,6 +5,11 @@ const DAGLink = dagPB.DAGLink const CID = require('cids') const { withTimeoutOption } = require('../../utils') +/** + * @param {any} node + * @param {DAGLink[]} [links] + * @returns {DAGLink[]} + */ function findLinks (node, links = []) { for (const key in node) { const val = node[key] @@ -35,8 +40,17 @@ function findLinks (node, links = []) { return links } +/** + * @param {Object} config + * @param {import('.').Dag} config.dag + */ module.exports = ({ dag }) => { - return withTimeoutOption(async function links (multihash, options = {}) { + /** + * @param {CID} multihash + * @param {import('.').AbortOptions} options + * @returns {Promise} + */ + async function links (multihash, options = {}) { const cid = new CID(multihash) const result = await dag.get(cid, options) @@ -53,5 +67,7 @@ module.exports = ({ dag }) => { } throw new Error(`Cannot resolve links from codec ${cid.codec}`) - }) + } + + return withTimeoutOption(links) } diff --git a/packages/ipfs-core/src/components/object/new.js b/packages/ipfs-core/src/components/object/new.js index f7e24fdbbc..04ed5f56c3 100644 --- a/packages/ipfs-core/src/components/object/new.js +++ b/packages/ipfs-core/src/components/object/new.js @@ -6,8 +6,18 @@ const multicodec = require('multicodec') const Unixfs = require('ipfs-unixfs') const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + */ module.exports = ({ ipld, preload }) => { - return withTimeoutOption(async function _new (options = {}) { + /** + * + * @param {NewOptions & AbortOptions} options + * @returns {Promise} + */ + async function _new (options = {}) { let data if (options.template) { @@ -33,5 +43,19 @@ module.exports = ({ ipld, preload }) => { } return cid - }) + } + + return withTimeoutOption(_new) } + +/** + * @typedef {Object} NewOptions + * @property {string} [template] + * @property {boolean} [recursive] + * @property {boolean} [nocache] + * @property {boolean} [preload] + * @property {string} [enc] + * + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/patch/add-link.js b/packages/ipfs-core/src/components/object/patch/add-link.js index 77f0d26708..933967b245 100644 --- a/packages/ipfs-core/src/components/object/patch/add-link.js +++ b/packages/ipfs-core/src/components/object/patch/add-link.js @@ -2,13 +2,21 @@ const { withTimeoutOption } = require('../../../utils') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + * @param {import('.').GCLock} config.gcLock + */ module.exports = ({ ipld, gcLock, preload }) => { const get = require('../get')({ ipld, preload }) const put = require('../put')({ ipld, gcLock, preload }) - return withTimeoutOption(async function addLink (multihash, link, options) { + async function addLink (multihash, link, options) { const node = await get(multihash, options) node.addLink(link) return put(node, options) - }) + } + + return withTimeoutOption(addLink) } diff --git a/packages/ipfs-core/src/components/object/patch/append-data.js b/packages/ipfs-core/src/components/object/patch/append-data.js index df31ca668c..9ab5126133 100644 --- a/packages/ipfs-core/src/components/object/patch/append-data.js +++ b/packages/ipfs-core/src/components/object/patch/append-data.js @@ -4,13 +4,20 @@ const { DAGNode } = require('ipld-dag-pb') const { withTimeoutOption } = require('../../../utils') const uint8ArrayConcat = require('uint8arrays/concat') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + * @param {import('.').GCLock} config.gcLock + */ module.exports = ({ ipld, gcLock, preload }) => { const get = require('../get')({ ipld, preload }) const put = require('../put')({ ipld, gcLock, preload }) - - return withTimeoutOption(async function appendData (multihash, data, options) { + async function appendData (multihash, data, options) { const node = await get(multihash, options) const newData = uint8ArrayConcat([node.Data, data]) return put(new DAGNode(newData, node.Links), options) - }) + } + + return withTimeoutOption(appendData) } diff --git a/packages/ipfs-core/src/components/object/patch/index.js b/packages/ipfs-core/src/components/object/patch/index.js new file mode 100644 index 0000000000..5166c6b33e --- /dev/null +++ b/packages/ipfs-core/src/components/object/patch/index.js @@ -0,0 +1,30 @@ +'use strict' + +const createAddLink = require('./add-link') +const createAppendData = require('./append-data') +const createRmLink = require('./rm-link') +const createSetData = require('./set-data') + +class ObjectPatchAPI { + /** + * @param {Object} config + * @param {IPLD} config.ipld + * @param {Preload} config.preload + * @param {GCLock} config.gcLock + */ + constructor ({ ipld, preload, gcLock }) { + this.addLink = createAddLink({ ipld, preload, gcLock }) + this.appendData = createAppendData({ ipld, preload, gcLock }) + this.rmLink = createRmLink({ ipld, preload, gcLock }) + this.setData = createSetData({ ipld, preload, gcLock }) + } +} +module.exports = ObjectPatchAPI + +/** + * @typedef {import('..').IPLD} IPLD + * @typedef {import('..').Preload} Preload + * @typedef {import('..').GCLock} GCLock + * @typedef {import('..').CID} CID + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/patch/rm-link.js b/packages/ipfs-core/src/components/object/patch/rm-link.js index 9d028017d0..920632fcf1 100644 --- a/packages/ipfs-core/src/components/object/patch/rm-link.js +++ b/packages/ipfs-core/src/components/object/patch/rm-link.js @@ -2,13 +2,21 @@ const { withTimeoutOption } = require('../../../utils') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + * @param {import('.').GCLock} config.gcLock + */ module.exports = ({ ipld, gcLock, preload }) => { const get = require('../get')({ ipld, preload }) const put = require('../put')({ ipld, gcLock, preload }) - return withTimeoutOption(async function rmLink (multihash, linkRef, options) { + async function rmLink (multihash, linkRef, options) { const node = await get(multihash, options) node.rmLink(linkRef.Name || linkRef.name) return put(node, options) - }) + } + + return withTimeoutOption(rmLink) } diff --git a/packages/ipfs-core/src/components/object/patch/set-data.js b/packages/ipfs-core/src/components/object/patch/set-data.js index 9a9e4b3a1f..94d272c693 100644 --- a/packages/ipfs-core/src/components/object/patch/set-data.js +++ b/packages/ipfs-core/src/components/object/patch/set-data.js @@ -3,12 +3,20 @@ const { DAGNode } = require('ipld-dag-pb') const { withTimeoutOption } = require('../../../utils') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + * @param {import('.').GCLock} config.gcLock + */ module.exports = ({ ipld, gcLock, preload }) => { const get = require('../get')({ ipld, preload }) const put = require('../put')({ ipld, gcLock, preload }) - return withTimeoutOption(async function setData (multihash, data, options) { + async function setData (multihash, data, options) { const node = await get(multihash, options) return put(new DAGNode(data, node.Links), options) - }) + } + + return withTimeoutOption(setData) } diff --git a/packages/ipfs-core/src/components/object/put.js b/packages/ipfs-core/src/components/object/put.js index cbbe4f2e23..e56812f4f2 100644 --- a/packages/ipfs-core/src/components/object/put.js +++ b/packages/ipfs-core/src/components/object/put.js @@ -46,8 +46,20 @@ function parseProtoBuffer (buf) { return dagPB.util.deserialize(buf) } +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + * @param {import('.').GCLock} config.gcLock + */ module.exports = ({ ipld, gcLock, preload }) => { - return withTimeoutOption(async function put (obj, options = {}) { + /** + * + * @param {Uint8Array|DAGNode|{ Data: any, links: DAGLink[]}} obj + * @param {PutOptions & AbortOptions} options + * @returns {Promise} + */ + async function put (obj, options = {}) { const encoding = options.enc let node @@ -82,5 +94,16 @@ module.exports = ({ ipld, gcLock, preload }) => { } finally { release() } - }) + } + + return withTimeoutOption(put) } + +/** + * @typedef {Object} PutOptions + * @property {boolean} [preload] + * @property {string} [enc] + * + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/object/stat.js b/packages/ipfs-core/src/components/object/stat.js index 511415c4c7..d4dba4aeb8 100644 --- a/packages/ipfs-core/src/components/object/stat.js +++ b/packages/ipfs-core/src/components/object/stat.js @@ -3,9 +3,22 @@ const dagPB = require('ipld-dag-pb') const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').IPLD} config.ipld + * @param {import('.').Preload} config.preload + */ module.exports = ({ ipld, preload }) => { const get = require('./get')({ ipld, preload }) - return withTimeoutOption(async function stat (multihash, options = {}) { + + /** + * Returns stats about an Object + * + * @param {CID} multihash + * @param {StatOptions & AbortOptions} options + * @returns {Promise} + */ + async function stat (multihash, options = {}) { const node = await get(multihash, options) const serialized = dagPB.util.serialize(node) const cid = await dagPB.util.cid(serialized, { @@ -23,5 +36,20 @@ module.exports = ({ ipld, preload }) => { DataSize: node.Data.length, CumulativeSize: blockSize + linkLength } - }) + } + + return withTimeoutOption(stat) } +/** + * @typedef {Object} Stat + * @property {string} Hash + * @property {number} NumLinks + * @property {number} BlockSize + * @property {number} LinksSize + * @property {number} DataSize + * @property {number} CumulativeSize + * + * @typedef {import('./get').GetOptions} StatOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/pin/add-all.js b/packages/ipfs-core/src/components/pin/add-all.js index 24889323e3..2c23f4db1f 100644 --- a/packages/ipfs-core/src/components/pin/add-all.js +++ b/packages/ipfs-core/src/components/pin/add-all.js @@ -4,18 +4,15 @@ const { resolvePath, withTimeoutOption } = require('../../utils') const PinManager = require('./pin-manager') const { PinTypes } = PinManager - -/** @type {(source:Source) => AsyncIterable} */ const normaliseInput = require('ipfs-core-utils/src/pins/normalise-input') /** - * * @param {Object} config - * @param {import('..').GCLock} config.gcLock - * @param {import('..').DAG} config.dag - * @param {import('./pin-manager')} config.pinManager + * @param {import('.').GCLock} config.gcLock + * @param {import('.').DagReader} config.dagReader + * @param {import('.').PinManager} config.pinManager */ -module.exports = ({ pinManager, gcLock, dag }) => { +module.exports = ({ gcLock, dagReader, pinManager }) => { /** * Adds multiple IPFS objects to the pinset and also stores it to the IPFS * repo. pinset is the set of hashes currently pinned (not gc'able) @@ -39,7 +36,7 @@ module.exports = ({ pinManager, gcLock, dag }) => { */ const pinAdd = async function * () { for await (const { path, recursive, metadata } of normaliseInput(source)) { - const cid = await resolvePath(dag, path) + const cid = await resolvePath(dagReader, path) // verify that each hash can be pinned const { reason } = await pinManager.isPinnedWithType(cid, [PinTypes.recursive, PinTypes.direct]) @@ -89,9 +86,9 @@ module.exports = ({ pinManager, gcLock, dag }) => { * @typedef {Object} AddSettings * @property {boolean} [lock] * - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions * - * @typedef {import('..').CID} CID + * @typedef {import('.').CID} CID */ /** diff --git a/packages/ipfs-core/src/components/pin/index.js b/packages/ipfs-core/src/components/pin/index.js new file mode 100644 index 0000000000..2e3ee7c66b --- /dev/null +++ b/packages/ipfs-core/src/components/pin/index.js @@ -0,0 +1,35 @@ +'use strict' + +const createAdd = require('./add') +const createAddAll = require('./add-all') +const createLs = require('./ls') +const createRm = require('./rm') +const createRmAll = require('./rm-all') + +class PinAPI { + /** + * @param {Object} config + * @param {GCLock} config.gcLock + * @param {DagReader} config.dagReader + * @param {PinManager} config.pinManager + */ + constructor ({ gcLock, dagReader, pinManager }) { + const addAll = createAddAll({ gcLock, dagReader, pinManager }) + this.addAll = addAll + this.add = createAdd({ addAll }) + const rmAll = createRmAll({ gcLock, dagReader, pinManager }) + this.rmAll = rmAll + this.rm = createRm({ rmAll }) + this.ls = createLs({ dagReader, pinManager }) + } +} +module.exports = PinAPI + +/** + * @typedef {import('..').Repo} Repo + * @typedef {import('..').GCLock} GCLock + * @typedef {import('..').DagReader} DagReader + * @typedef {import('..').PinManager} PinManager + * @typedef {import('..').AbortOptions} AbortOptions + * @typedef {import('..').CID} CID + */ diff --git a/packages/ipfs-core/src/components/pin/ls.js b/packages/ipfs-core/src/components/pin/ls.js index 179585d620..641acefda6 100644 --- a/packages/ipfs-core/src/components/pin/ls.js +++ b/packages/ipfs-core/src/components/pin/ls.js @@ -21,10 +21,10 @@ function toPin (type, cid, metadata) { /** * @param {Object} config - * @param {import('./pin-manager')} config.pinManager - * @param {import('../index').DAG} config.dag + * @param {import('.').PinManager} config.pinManager + * @param {import('.').DagReader} config.dagReader */ -module.exports = ({ pinManager, dag }) => { +module.exports = ({ pinManager, dagReader }) => { /** * List all the objects pinned to local storage * @@ -73,7 +73,7 @@ module.exports = ({ pinManager, dag }) => { let matched = false for await (const { path } of normaliseInput(options.paths)) { - const cid = await resolvePath(dag, path) + const cid = await resolvePath(dagReader, path) const { reason, pinned, parent, metadata } = await pinManager.isPinnedWithType(cid, type) if (!pinned) { @@ -137,6 +137,6 @@ module.exports = ({ pinManager, dag }) => { * @typedef {import('./pin-manager').PinType} PinType * @typedef {import('./pin-manager').PinQueryType} PinQueryType * - * @typedef {import('../../utils').AbortOptions} AbortOptions - * @typedef {import('..').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID */ diff --git a/packages/ipfs-core/src/components/pin/pin-manager.js b/packages/ipfs-core/src/components/pin/pin-manager.js index 9c99ee5d15..8155d35460 100644 --- a/packages/ipfs-core/src/components/pin/pin-manager.js +++ b/packages/ipfs-core/src/components/pin/pin-manager.js @@ -49,14 +49,25 @@ const PinTypes = { } class PinManager { - constructor (repo, dag) { + /** + * @param {Object} config + * @param {import('.').Repo} config.repo + * @param {import('.').DagReader} config.dagReader + */ + constructor ({ repo, dagReader }) { this.repo = repo - this.dag = dag + this.dag = dagReader this.log = debug('ipfs:pin') this.directPins = new Set() this.recursivePins = new Set() } + /** + * @private + * @param {CID} cid + * @param {Object} [options] + * @param {boolean} [options.preload] + */ async * _walkDag (cid, { preload = false }) { const { value: node } = await this.dag.get(cid, { preload }) @@ -73,6 +84,11 @@ class PinManager { } } + /** + * @param {CID} cid + * @param {PinOptions & AbortOptions} [options] + * @returns {Promise} + */ async pinDirectly (cid, options = {}) { await this.dag.get(cid, options) @@ -95,10 +111,21 @@ class PinManager { return this.repo.pins.put(cidToKey(cid), cbor.encode(pin)) } - async unpin (cid) { // eslint-disable-line require-await + /** + * @param {CID} cid + * @param {AbortOptions} [options] + * @returns {Promise} + */ + // eslint-disable-next-line require-await + async unpin (cid, options) { return this.repo.pins.delete(cidToKey(cid)) } + /** + * @param {CID} cid + * @param {PreloadOptions & PinOptions & AbortOptions} [options] + * @returns {Promise} + */ async pinRecursively (cid, options = {}) { await this.fetchCompleteDag(cid, options) @@ -121,7 +148,11 @@ class PinManager { await this.repo.pins.put(cidToKey(cid), cbor.encode(pin)) } - async * directKeys () { + /** + * @param {AbortOptions} [options] + * @returns AsyncIterable<{ cid: CID, metadata: any }> + */ + async * directKeys (options) { for await (const entry of this.repo.pins.query({ filters: [(entry) => { const pin = cbor.decode(entry.value) @@ -141,7 +172,11 @@ class PinManager { } } - async * recursiveKeys () { + /** + * @param {AbortOptions} [options] + * @returns {AsyncIterable<{ cid: CID, metadata: any }>} + */ + async * recursiveKeys (options) { for await (const entry of this.repo.pins.query({ filters: [(entry) => { const pin = cbor.decode(entry.value) @@ -184,7 +219,12 @@ class PinManager { } } - async isPinnedWithType (cid, types) { + /** + * @param {CID} cid + * @param {PinQueryType|PinQueryType[]} types + * @param {AbortOptions} [options] + */ + async isPinnedWithType (cid, types, options) { if (!Array.isArray(types)) { types = [types] } @@ -256,18 +296,38 @@ class PinManager { } } + /** + * @param {CID} cid + * @param {PreloadOptions & AbortOptions} options + */ async fetchCompleteDag (cid, options) { await all(this._walkDag(cid, { preload: options.preload })) } - // Throws an error if the pin type is invalid + /** + * Throws an error if the pin type is invalid + * + * @param {any} type + * @returns {type is PinType} + */ static checkPinType (type) { if (typeof type !== 'string' || !Object.keys(PinTypes).includes(type)) { throw invalidPinTypeErr(type) } + return true } } PinManager.PinTypes = PinTypes module.exports = PinManager + +/** + * @typedef {Object} PinOptions + * @property {any} [metadata] + * + * @typedef {Object} PreloadOptions + * @property {boolean} [preload] + * + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/pin/rm-all.js b/packages/ipfs-core/src/components/pin/rm-all.js index 06f0887fe9..a7f7e117ad 100644 --- a/packages/ipfs-core/src/components/pin/rm-all.js +++ b/packages/ipfs-core/src/components/pin/rm-all.js @@ -6,16 +6,16 @@ const { PinTypes } = require('./pin-manager') /** * @param {Object} config - * @param {import('./pin-manager')} config.pinManager - * @param {import('..').GCLock} config.gcLock - * @param {import('..').DAG} config.dag + * @param {import('.').PinManager} config.pinManager + * @param {import('.').GCLock} config.gcLock + * @param {import('.').DagReader} config.dagReader */ -module.exports = ({ pinManager, gcLock, dag }) => { +module.exports = ({ pinManager, gcLock, dagReader }) => { /** * Unpin one or more blocks from your repo * * @param {Source} source - Unpin all pins from the source - * @param {AbortOptions} [_options] + * @param {AbortOptions} [options] * @returns {AsyncIterable} * @example * ```js @@ -29,13 +29,13 @@ module.exports = ({ pinManager, gcLock, dag }) => { * // CID('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u') * ``` */ - async function * rmAll (source, _options = {}) { + async function * rmAll (source, options = {}) { const release = await gcLock.readLock() try { // verify that each hash can be unpinned for await (const { path, recursive } of normaliseInput(source)) { - const cid = await resolvePath(dag, path) + const cid = await resolvePath(dagReader, path) const { pinned, reason } = await pinManager.isPinnedWithType(cid, PinTypes.all) if (!pinned) { @@ -72,7 +72,7 @@ module.exports = ({ pinManager, gcLock, dag }) => { } /** - * @typedef {import('..').CID} CID - * @typedef {import('../../utils').AbortOptions} AbortOptions + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions * @typedef {import('./add-all').Source} Source */ diff --git a/packages/ipfs-core/src/components/ping.js b/packages/ipfs-core/src/components/ping.js index 24ed95dbff..ca14d06ead 100644 --- a/packages/ipfs-core/src/components/ping.js +++ b/packages/ipfs-core/src/components/ping.js @@ -7,9 +7,9 @@ const { withTimeoutOption } = require('../utils') /** * @param {Object} config - * @param {import('libp2p')} config.libp2p + * @param {import('.').NetworkService} config.network */ -module.exports = ({ libp2p }) => { +module.exports = ({ network }) => { /** * Send echo request packets to IPFS hosts. * @@ -28,6 +28,7 @@ module.exports = ({ libp2p }) => { * ``` */ async function * ping (peerId, options = {}) { + const { libp2p } = await network.use() options.count = options.count || 10 if (!PeerId.isPeerId(peerId)) { @@ -92,5 +93,5 @@ module.exports = ({ libp2p }) => { * @typedef {Object} PingSettings * @property {number} [count=10] - The number of ping messages to send * - * @typedef {import('../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/pubsub.js b/packages/ipfs-core/src/components/pubsub.js index 531a85e359..cc50eed07a 100644 --- a/packages/ipfs-core/src/components/pubsub.js +++ b/packages/ipfs-core/src/components/pubsub.js @@ -2,18 +2,149 @@ const { withTimeoutOption } = require('../utils') const errCode = require('err-code') +const { NotEnabledError } = require('../errors') +const get = require('dlv') + +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + * @param {import('.').IPFSConfig} [config.config] + */ +module.exports = ({ network, config }) => { + const isEnabled = get(config, 'Pubsub.Enabled', true) -module.exports = ({ libp2p }) => { return { - subscribe: withTimeoutOption((...args) => libp2p.pubsub.subscribe(...args)), - unsubscribe: withTimeoutOption((...args) => libp2p.pubsub.unsubscribe(...args)), - publish: withTimeoutOption(async (topic, data, _options) => { - if (!data) { - throw errCode(new Error('argument "data" is required'), 'ERR_ARG_REQUIRED') - } - await libp2p.pubsub.publish(topic, data) - }), - ls: withTimeoutOption((...args) => libp2p.pubsub.getTopics(...args)), - peers: withTimeoutOption((...args) => libp2p.pubsub.getSubscribers(...args)) + subscribe: isEnabled ? withTimeoutOption(subscribe) : notEnabled, + unsubscribe: isEnabled ? withTimeoutOption(unsubscribe) : notEnabled, + publish: isEnabled ? withTimeoutOption(publish) : notEnabled, + ls: isEnabled ? withTimeoutOption(ls) : notEnabled, + peers: isEnabled ? withTimeoutOption(peers) : notEnabled + } + + /** + * Subscribe to a pubsub topic. + * + * @example + * ```js + * const topic = 'fruit-of-the-day' + * const receiveMsg = (msg) => console.log(msg.data.toString()) + * + * await ipfs.pubsub.subscribe(topic, receiveMsg) + * console.log(`subscribed to ${topic}`) + * ``` + * + * @param {string} topic - The topic name + * @param {(message:Message) => void} handler - Event handler which will be + * called with a message object everytime one is received. + * @param {AbortOptions} [options] + * @returns {Promise} + */ + async function subscribe (topic, handler, options) { + const { libp2p } = await network.use(options) + return libp2p.pubsub.subscribe(topic, handler, options) + } + + /** + * Unsubscribes from a pubsub topic. + * + * @example + * ```js + * const topic = 'fruit-of-the-day' + * const receiveMsg = (msg) => console.log(msg.toString()) + * + * await ipfs.pubsub.subscribe(topic, receiveMsg) + * console.log(`subscribed to ${topic}`) + * + * await ipfs.pubsub.unsubscribe(topic, receiveMsg) + * console.log(`unsubscribed from ${topic}`) + * + * // Or removing all listeners: + * + * const topic = 'fruit-of-the-day' + * const receiveMsg = (msg) => console.log(msg.toString()) + * await ipfs.pubsub.subscribe(topic, receiveMsg); + * // Will unsubscribe ALL handlers for the given topic + * await ipfs.pubsub.unsubscribe(topic); + * ``` + * + * @param {string} topic - The topic to unsubscribe from + * @param {(message:Message) => void} [handler] - The handler to remove. If + * not provided unsubscribes al handlers for the topic. + * @param {AbortOptions} [options] + * @returns {Promise} + */ + async function unsubscribe (topic, handler, options) { + const { libp2p } = await network.use(options) + libp2p.pubsub.unsubscribe(topic, handler, options) + } + + /** + * Publish a data message to a pubsub topic. + * + * @example + * ```js + * const topic = 'fruit-of-the-day' + * const msg = new TextEncoder().encode('banana') + * + * await ipfs.pubsub.publish(topic, msg) + * // msg was broadcasted + * console.log(`published to ${topic}`) + * ``` + * + * @param {string} topic + * @param {Uint8Array} data + * @param {AbortOptions} options + * @returns {Promise} + */ + async function publish (topic, data, options) { + const { libp2p } = await network.use(options) + if (!data) { + throw errCode(new Error('argument "data" is required'), 'ERR_ARG_REQUIRED') + } + await libp2p.pubsub.publish(topic, data) + } + /** + * Returns the list of subscriptions the peer is subscribed to. + * + * @param {AbortOptions} [options] + * @returns {Promise} + */ + async function ls (options) { + const { libp2p } = await network.use(options) + return libp2p.pubsub.getTopics(options) + } + + /** + * Returns the peers that are subscribed to one topic. + * + * @example + * ```js + * const topic = 'fruit-of-the-day' + * + * const peerIds = await ipfs.pubsub.peers(topic) + * console.log(peerIds) + * ``` + * + * @param {string} topic + * @param {AbortOptions} [options] + * @returns {Promise} - An array of peer IDs subscribed to the topic + */ + async function peers (topic, options) { + const { libp2p } = await network.use(options) + return libp2p.pubsub.getSubscribers(topic, options) } } + +const notEnabled = async () => { // eslint-disable-line require-await + throw new NotEnabledError('pubsub not enabled') +} + +/** + * @typedef {Object} Message + * @property {string} from + * @property {Uint8Array} seqno + * @property {Uint8Array} data + * @property {string[]} topicIDs + * + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/refs/index.js b/packages/ipfs-core/src/components/refs/index.js index 669fd0cbe1..17557f67af 100644 --- a/packages/ipfs-core/src/components/refs/index.js +++ b/packages/ipfs-core/src/components/refs/index.js @@ -13,8 +13,21 @@ const Format = { edges: ' -> ' } +/** + * @param {Object} config + * @param {import('..').IPLD} config.ipld + * @param {import('..').Resolve} config.resolve + * @param {import('..').Preload} config.preload + */ module.exports = function ({ ipld, resolve, preload }) { - return withTimeoutOption(async function * refs (ipfsPath, options = {}) { // eslint-disable-line require-await + /** + * Get links (references) from an object + * + * @param {CID|string} ipfsPath - The object to search for references + * @param {RefsOptions & AbortOptions} [options] + * @returns {AsyncIterable} + */ + async function * refs (ipfsPath, options = {}) { if (options.maxDepth === 0) { return } @@ -30,12 +43,16 @@ module.exports = function ({ ipld, resolve, preload }) { } const rawPaths = Array.isArray(ipfsPath) ? ipfsPath : [ipfsPath] + + // @ts-ignore const paths = rawPaths.map(p => getFullPath(preload, p, options)) for (const path of paths) { yield * refsStream(resolve, ipld, path, options) } - }) + } + + return withTimeoutOption(refs) } module.exports.Format = Format @@ -160,3 +177,17 @@ function getNodeLinks (node, path = '') { } return links } + +/** + * @typedef {Object} RefsOptions + * @property {boolean} [recursive=false] - Recursively list references of child nodes + * @property {boolean} [unique=false] - Omit duplicate references from output + * @property {string} [format=''] - Output edges with given format. Available tokens: ``, ``, `` + * @property {boolean} [edges=false] - output references in edge format: `" -> "` + * @property {number} [maxDepth=1] - only for recursive refs, limits fetch and listing to the given depth + * + * @typedef {{ref:string, err?:null}|{ref?:undefined, err:Error}} RefResult + * + * @typedef {import('..').AbortOptions} AbortOptions + * @typedef {import('..').Repo} Repo + */ diff --git a/packages/ipfs-core/src/components/refs/local.js b/packages/ipfs-core/src/components/refs/local.js index 871c5440dd..e98a8733f8 100644 --- a/packages/ipfs-core/src/components/refs/local.js +++ b/packages/ipfs-core/src/components/refs/local.js @@ -2,10 +2,21 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Repo} config.repo + */ module.exports = function ({ repo }) { - return withTimeoutOption(async function * refsLocal (options = {}) { + /** + * @param {import('.').AbortOptions} [options] + * @returns {AsyncIterable<{ref: string}>} + */ + async function * refsLocal (options = {}) { + // @ts-ignore - TS is not aware of keysOnly for await (const cid of repo.blocks.query({ keysOnly: true, signal: options.signal })) { yield { ref: cid.toString() } } - }) + } + + return withTimeoutOption(refsLocal) } diff --git a/packages/ipfs-core/src/components/repo/gc.js b/packages/ipfs-core/src/components/repo/gc.js index c2b8b26afa..bfa0d4e519 100644 --- a/packages/ipfs-core/src/components/repo/gc.js +++ b/packages/ipfs-core/src/components/repo/gc.js @@ -15,10 +15,10 @@ const BLOCK_RM_CONCURRENCY = 256 * Perform mark and sweep garbage collection * * @param {Object} config - * @param {import('..').GCLock} config.gcLock - * @param {import('..').Pin} config.pin - * @param {import('..').Refs} config.refs - * @param {import('..').IPFSRepo} config.repo + * @param {import('.').GCLock} config.gcLock + * @param {import('.').Pin} config.pin + * @param {import('.').Refs} config.refs + * @param {import('.').Repo} config.repo */ module.exports = ({ gcLock, pin, refs, repo }) => { /** @@ -35,6 +35,7 @@ module.exports = ({ gcLock, pin, refs, repo }) => { // Mark all blocks that are being used const markedSet = await createMarkedSet({ pin, refs, repo }) // Get all blocks keys from the blockstore + // @ts-ignore - TS is not aware of keysOnly overload const blockKeys = repo.blocks.query({ keysOnly: true }) // Delete blocks that are not being used diff --git a/packages/ipfs-core/src/components/repo/index.js b/packages/ipfs-core/src/components/repo/index.js new file mode 100644 index 0000000000..82e4e401d5 --- /dev/null +++ b/packages/ipfs-core/src/components/repo/index.js @@ -0,0 +1,29 @@ +'use strict' + +const createGC = require('./gc') +const createStat = require('./stat') +const createVersion = require('./version') + +class RepoAPI { + /** + * @param {Object} config + * @param {GCLock} config.gcLock + * @param {Pin} config.pin + * @param {Repo} config.repo + * @param {Refs} config.refs + */ + constructor ({ gcLock, pin, repo, refs }) { + this.gc = createGC({ gcLock, pin, refs, repo }) + this.stat = createStat({ repo }) + this.version = createVersion({ repo }) + } +} +module.exports = RepoAPI + +/** + * @typedef {import('..').GCLock} GCLock + * @typedef {import('..').Pin} Pin + * @typedef {import('..').Repo} Repo + * @typedef {import('..').Refs} Refs + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/repo/stat.js b/packages/ipfs-core/src/components/repo/stat.js index 9194a00ded..afeff552dc 100644 --- a/packages/ipfs-core/src/components/repo/stat.js +++ b/packages/ipfs-core/src/components/repo/stat.js @@ -2,8 +2,15 @@ const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Repo} config.repo + */ module.exports = ({ repo }) => { - return withTimeoutOption(async function stat (options) { + /** + * @param {import('.').AbortOptions} [options] + */ + async function stat (options) { const stats = await repo.stat(options) return { @@ -13,5 +20,7 @@ module.exports = ({ repo }) => { version: stats.version.toString(), storageMax: stats.storageMax } - }) + } + + return withTimeoutOption(stat) } diff --git a/packages/ipfs-core/src/components/repo/version.js b/packages/ipfs-core/src/components/repo/version.js index 4622f667b7..e31390490a 100644 --- a/packages/ipfs-core/src/components/repo/version.js +++ b/packages/ipfs-core/src/components/repo/version.js @@ -3,15 +3,21 @@ const { repoVersion } = require('ipfs-repo') const { withTimeoutOption } = require('../../utils') +/** + * @param {Object} config + * @param {import('.').Repo} config.repo + */ module.exports = ({ repo }) => { /** * If the repo has been initialized, report the current version. * Otherwise report the version that would be initialized. * - * @returns {number} + * @param options + * @returns {Promise} */ - return withTimeoutOption(async function version (options) { + async function version (options) { try { + // @ts-ignore - not a public API await repo._checkInitialized(options) } catch (err) { // TODO: (dryajov) This is really hacky, there must be a better way @@ -30,5 +36,7 @@ module.exports = ({ repo }) => { } return repo.version.get(options) - }) + } + + return withTimeoutOption(version) } diff --git a/packages/ipfs-core/src/components/resolve.js b/packages/ipfs-core/src/components/resolve.js index 9b14d4516c..2a5b890bd4 100644 --- a/packages/ipfs-core/src/components/resolve.js +++ b/packages/ipfs-core/src/components/resolve.js @@ -7,8 +7,8 @@ const { withTimeoutOption } = require('../utils') /** * @param {Object} config - * @param {import('.').IPLD} config.ipld - An instance of IPLD - * @param {import('.').Name} [config.name] - An IPFS core interface name API + * @param {import('.').IPLD} config.ipld + * @param {import('.').Name} config.name - An IPFS core interface name API */ module.exports = ({ ipld, name }) => { /** @@ -102,5 +102,5 @@ module.exports = ({ ipld, name }) => { * @property {boolean} [recursive=true] - Resolve until result is an IPFS name. * @property {string} [cidBase='base58btc'] - Multibase codec name the CID in the resolved path will be encoded with. * - * @typedef {import('../utils').AbortOptions} AbortOptions + * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/start.js b/packages/ipfs-core/src/components/start.js index 1f1f286eb2..63b6378443 100644 --- a/packages/ipfs-core/src/components/start.js +++ b/packages/ipfs-core/src/components/start.js @@ -1,404 +1,32 @@ 'use strict' -const log = require('debug')('ipfs:components:start') -const Bitswap = require('ipfs-bitswap') -const multiaddr = require('multiaddr') -const get = require('dlv') -const defer = require('p-defer') -const errCode = require('err-code') -const IPNS = require('../ipns') -const routingConfig = require('../ipns/routing/config') -const { AlreadyInitializedError, NotEnabledError } = require('../errors') -const Components = require('./') -const createMfsPreload = require('../mfs-preload') -const { withTimeoutOption } = require('../utils') - -const WEBSOCKET_STAR_PROTO_CODE = 479 +const Service = require('../utils/service') /** * @param {Object} config - * @param {APIManager} config.apiManager - * @param {StartOptions} config.options - * @param {IPFSBlockService} config.blockService - * @param {GCLock} config.gcLock - * @param {InitOptions} config.initOptions - * @param {IPLD} config.ipld - * @param {Keychain} config.keychain - * @param {PeerId} config.peerId - * @param {PinManager} config.pinManager - * @param {Preload} config.preload - * @param {Print} config.print - * @param {IPFSRepo} config.repo + * @param {import('.').NetworkService} config.network + * @param {import('.').PeerId} config.peerId + * @param {import('.').Repo} config.repo + * @param {import('.').BlockService} config.blockService + * @param {import('.').Print} config.print + * @param {import('.').Preload} config.preload + * @param {import('.').MFSPreload} config.mfsPreload + * @param {import('.').IPNS} config.ipns + * @param {import('.').Keychain} config.keychain + * @param {string} [config.pass] */ -module.exports = ({ - apiManager, - options: constructorOptions, - blockService, - gcLock, - initOptions, - ipld, - keychain, - peerId, - pinManager, - preload, - print, - repo -}) => { - async function start () { - const startPromise = defer() - startPromise.promise.catch((err) => log(err)) - - const { cancel } = apiManager.update({ start: () => startPromise.promise }) - - try { - // The repo may be closed if previously stopped - if (repo.closed) { - await repo.open() - } - - const config = await repo.config.getAll() - const addrs = [] - - if (config.Addresses && config.Addresses.Swarm) { - config.Addresses.Swarm.forEach(addr => { - let ma = multiaddr(addr) - - // Temporary error for users migrating using websocket-star multiaddrs for listenning on libp2p - // websocket-star support was removed from ipfs and libp2p - if (ma.protoCodes().includes(WEBSOCKET_STAR_PROTO_CODE)) { - throw errCode(new Error('websocket-star swarm addresses are not supported. See https://github.com/ipfs/js-ipfs/issues/2779'), 'ERR_WEBSOCKET_STAR_SWARM_ADDR_NOT_SUPPORTED') - } - - // multiaddrs that go via a signalling server or other intermediary (e.g. stardust, - // webrtc-star) can have the intermediary's peer ID in the address, so append our - // peer ID to the end of it - const maId = ma.getPeerId() - if (maId && maId !== peerId.toB58String()) { - ma = ma.encapsulate(`/p2p/${peerId.toB58String()}`) - } - - addrs.push(ma) - }) - } - - const libp2p = Components.libp2p({ - options: constructorOptions, - repo, - peerId: peerId, - multiaddrs: addrs, - config - }) - - libp2p.keychain && await libp2p.loadKeychain() - - await libp2p.start() - - libp2p.transportManager.getAddrs().forEach(ma => print(`Swarm listening on ${ma}/p2p/${peerId.toB58String()}`)) - - const ipnsRouting = routingConfig({ libp2p, repo, peerId, options: constructorOptions }) - const ipns = new IPNS(ipnsRouting, repo.datastore, peerId, keychain, { pass: initOptions.pass }) - const bitswap = new Bitswap(libp2p, repo.blocks, { statsEnabled: true }) - - await bitswap.start() - - blockService.setExchange(bitswap) - - const dag = { - get: Components.dag.get({ ipld, preload }), - resolve: Components.dag.resolve({ ipld, preload }), - tree: Components.dag.tree({ ipld, preload }), - // FIXME: resolve this circular dependency - get put () { - const put = Components.dag.put({ ipld, pin, gcLock, preload }) - Object.defineProperty(this, 'put', { value: put }) - return put - } - } - - const pinAddAll = Components.pin.addAll({ pinManager, gcLock, dag }) - const pinRmAll = Components.pin.rmAll({ pinManager, gcLock, dag }) - - const pin = { - add: Components.pin.add({ addAll: pinAddAll }), - addAll: pinAddAll, - ls: Components.pin.ls({ pinManager, dag }), - rm: Components.pin.rm({ rmAll: pinRmAll }), - rmAll: pinRmAll - } - - const block = { - get: Components.block.get({ blockService, preload }), - put: Components.block.put({ blockService, pin, gcLock, preload }), - rm: Components.block.rm({ blockService, gcLock, pinManager }), - stat: Components.block.stat({ blockService, preload }) - } - - const files = Components.files({ ipld, block, blockService, repo, preload, options: constructorOptions }) - const mfsPreload = createMfsPreload({ files, preload, options: constructorOptions.preload }) - - await Promise.all([ - ipns.republisher.start(), - preload.start(), - mfsPreload.start() - ]) - - const api = createApi({ - apiManager, - bitswap, - block, - blockService, - config, - constructorOptions, - dag, - files, - gcLock, - initOptions, - ipld, - ipns, - keychain, - libp2p, - mfsPreload, - peerId, - pin, - preload, - print, - repo - }) - - const { api: startedApi } = apiManager.update(api, () => undefined) - startPromise.resolve(startedApi) - return startedApi - } catch (err) { - cancel() - startPromise.reject(err) - throw err - } - } - return withTimeoutOption(start) -} - -/** - * @param {CreateAPIConfig} config - */ -function createApi ({ - apiManager, - bitswap, - block, - blockService, - config, - constructorOptions, - dag, - files, - gcLock, - initOptions, - ipld, - ipns, - keychain, - libp2p, - mfsPreload, - peerId, - pin, - preload, - print, - repo -}) { - const object = { - data: Components.object.data({ ipld, preload }), - get: Components.object.get({ ipld, preload }), - links: Components.object.links({ dag }), - new: Components.object.new({ ipld, preload }), - patch: { - addLink: Components.object.patch.addLink({ ipld, gcLock, preload }), - appendData: Components.object.patch.appendData({ ipld, gcLock, preload }), - rmLink: Components.object.patch.rmLink({ ipld, gcLock, preload }), - setData: Components.object.patch.setData({ ipld, gcLock, preload }) - }, - put: Components.object.put({ ipld, gcLock, preload }), - stat: Components.object.stat({ ipld, preload }) - } - - const addAll = Components.addAll({ block, preload, pin, gcLock, options: constructorOptions }) - const isOnline = Components.isOnline({ libp2p }) - - const dhtNotEnabled = async () => { // eslint-disable-line require-await - throw new NotEnabledError('dht not enabled') - } - - const dhtNotEnabledIterator = async function * () { // eslint-disable-line require-await,require-yield - throw new NotEnabledError('dht not enabled') - } - - const dht = get(libp2p, '_config.dht.enabled', false) ? Components.dht({ libp2p, repo }) : { - get: dhtNotEnabled, - put: dhtNotEnabled, - findProvs: dhtNotEnabledIterator, - findPeer: dhtNotEnabled, - provide: dhtNotEnabledIterator, - query: dhtNotEnabledIterator - } - - const dns = Components.dns() - const name = { - pubsub: { - cancel: Components.name.pubsub.cancel({ ipns, options: constructorOptions }), - state: Components.name.pubsub.state({ ipns, options: constructorOptions }), - subs: Components.name.pubsub.subs({ ipns, options: constructorOptions }) - }, - publish: Components.name.publish({ ipns, dag, peerId, isOnline, keychain }), - resolve: Components.name.resolve({ dns, ipns, peerId, isOnline, options: constructorOptions }) - } - const resolve = Components.resolve({ name, ipld }) - const refs = Object.assign( - Components.refs({ ipld, resolve, preload }), - { local: Components.refs.local({ repo }) } - ) - - const pubsubNotEnabled = async () => { // eslint-disable-line require-await - throw new NotEnabledError('pubsub not enabled') - } - - const pubsub = get(constructorOptions, 'config.Pubsub.Enabled', get(config, 'Pubsub.Enabled', true)) - ? Components.pubsub({ libp2p }) - : { - subscribe: pubsubNotEnabled, - unsubscribe: pubsubNotEnabled, - publish: pubsubNotEnabled, - ls: pubsubNotEnabled, - peers: pubsubNotEnabled - } - - const api = { - add: Components.add({ addAll }), - addAll, - bitswap: { - stat: Components.bitswap.stat({ bitswap }), - unwant: Components.bitswap.unwant({ bitswap }), - wantlist: Components.bitswap.wantlist({ bitswap }), - wantlistForPeer: Components.bitswap.wantlistForPeer({ bitswap }) - }, - block, - bootstrap: { - add: Components.bootstrap.add({ repo }), - clear: Components.bootstrap.clear({ repo }), - list: Components.bootstrap.list({ repo }), - reset: Components.bootstrap.reset({ repo }), - rm: Components.bootstrap.rm({ repo }) - }, - cat: Components.cat({ ipld, preload }), - config: Components.config({ repo }), - dag, - dht, - dns, - files, - get: Components.get({ ipld, preload }), - id: Components.id({ peerId, libp2p }), - init: async () => { throw new AlreadyInitializedError() }, // eslint-disable-line require-await - isOnline, - ipld, - key: { - export: Components.key.export({ keychain }), - gen: Components.key.gen({ keychain }), - import: Components.key.import({ keychain }), - info: Components.key.info({ keychain }), - list: Components.key.list({ keychain }), - rename: Components.key.rename({ keychain }), - rm: Components.key.rm({ keychain }) - }, - libp2p, - ls: Components.ls({ ipld, preload }), - name, - object, - pin, - ping: Components.ping({ libp2p }), - pubsub, - refs, - repo: { - gc: Components.repo.gc({ gcLock, pin, refs, repo }), - stat: Components.repo.stat({ repo }), - version: Components.repo.version({ repo }) - }, - resolve, - start: () => apiManager.api, - stats: { - bitswap: Components.bitswap.stat({ bitswap }), - bw: libp2p.metrics - ? Components.stats.bw({ libp2p }) - : async () => { // eslint-disable-line require-await - throw new NotEnabledError('libp2p metrics not enabled') - }, - repo: Components.repo.stat({ repo }) - }, - stop: Components.stop({ - apiManager, - bitswap, - options: constructorOptions, - blockService, - gcLock, - initOptions, - ipld, - ipns, - keychain, - libp2p, - mfsPreload, +module.exports = ({ network, preload, peerId, keychain, repo, ipns, blockService, print, pass }) => { + const start = async () => { + const { bitswap, libp2p } = await Service.start(network, { peerId, - preload, + repo, print, - repo - }), - swarm: { - addrs: Components.swarm.addrs({ libp2p }), - connect: Components.swarm.connect({ libp2p }), - disconnect: Components.swarm.disconnect({ libp2p }), - localAddrs: Components.swarm.localAddrs({ multiaddrs: libp2p.multiaddrs }), - peers: Components.swarm.peers({ libp2p }) - }, - version: Components.version({ repo }) + pass + }) + ipns.startOnline({ keychain, libp2p, peerId, repo }) + preload.start() + blockService.setExchange(bitswap) } - return api + return start } - -/** - * @typedef {Object} CreateAPIConfig - * @property {APIManager} apiManager - * @property {Bitswap} [bitswap] - * @property {Block} block - * @property {IPFSBlockService} blockService - * @property {Config} config - * @property {StartOptions} constructorOptions - * @property {DAG} dag - * @property {Files} [files] - * @property {GCLock} gcLock - * @property {InitOptions} initOptions - * @property {IPLD} ipld - * @property {import('../ipns')} ipns - * @property {Keychain} keychain - * @property {LibP2P} libp2p - * @property {MFSPreload} mfsPreload - * @property {PeerId} peerId - * @property {Pin} pin - * @property {Preload} preload - * @property {Print} print - * @property {IPFSRepo} repo - * - * @typedef {(...args:any[]) => void} Print - * - * @typedef {import('./init').InitOptions} InitOptions - * @typedef {import('./init').ConstructorOptions} StartOptions - * @typedef {import('./init').Keychain} Keychain - * @typedef {import('../api-manager')} APIManager - * @typedef {import('./pin/pin-manager')} PinManager - * @typedef {import('../mfs-preload').MFSPreload} MFSPreload - * @typedef {import('.').IPFSBlockService} IPFSBlockService - * @typedef {import('.').GCLock} GCLock - * @typedef {import('.')} IPLD - * @typedef {import('.').PeerId} PeerId - * @typedef {import('.').Preload} Preload - * @typedef {import('.').IPFSRepo} IPFSRepo - * @typedef {import('.').LibP2P} LibP2P - * @typedef {import('.').Pin} Pin - * @typedef {import('.').Files} Files - * @typedef {import('.').DAG} DAG - * @typedef {import('.').Config} Config - * @typedef {import('.').Block} Block - */ diff --git a/packages/ipfs-core/src/components/stats/bw.js b/packages/ipfs-core/src/components/stats/bw.js index 3c2319187c..36351858db 100644 --- a/packages/ipfs-core/src/components/stats/bw.js +++ b/packages/ipfs-core/src/components/stats/bw.js @@ -5,6 +5,11 @@ const parseDuration = require('parse-duration').default const errCode = require('err-code') const { withTimeoutOption } = require('../../utils') +/** + * @param {LibP2P} libp2p + * @param {BWOptions} opts + * @returns {BandwidthInfo} + */ function getBandwidthStats (libp2p, opts) { let stats @@ -35,17 +40,30 @@ function getBandwidthStats (libp2p, opts) { } } -module.exports = ({ libp2p }) => { - return withTimeoutOption(async function * (options = {}) { +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + */ +module.exports = ({ network }) => { + /** + * Get IPFS bandwidth information + * + * @param {BWOptions & AbortOptions} options + * @returns {AsyncIterable} + */ + const bw = async function * (options = {}) { + const { libp2p } = await network.use(options) + if (!options.poll) { yield getBandwidthStats(libp2p, options) return } - let interval = options.interval || 1000 + const interval = options.interval || 1000 + let ms = -1 try { - interval = typeof interval === 'string' ? parseDuration(interval) : interval - if (!interval || interval < 0) throw new Error('invalid duration') + ms = typeof interval === 'string' ? parseDuration(interval) || -1 : interval + if (!ms || ms < 0) throw new Error('invalid duration') } catch (err) { throw errCode(err, 'ERR_INVALID_POLL_INTERVAL') } @@ -55,10 +73,31 @@ module.exports = ({ libp2p }) => { while (true) { yield getBandwidthStats(libp2p, options) // eslint-disable-next-line no-loop-func - await new Promise(resolve => { timeoutId = setTimeout(resolve, interval) }) + await new Promise(resolve => { timeoutId = setTimeout(resolve, ms) }) } } finally { clearTimeout(timeoutId) } - }) + } + + return withTimeoutOption(bw) } + +/** + * @typedef {Object} BWOptions + * @property {PeerId|CID|string} [peer] - Specifies a peer to print bandwidth for + * @property {string} [proto] - Specifies a protocol to print bandwidth for + * @property {boolean} [poll] - Is used to yield bandwidth info at an interval + * @property {number|string} [interval=1000] - The time interval to wait between updating output, if `poll` is `true`. + * + * @typedef {Object} BandwidthInfo + * @property {Big} totalIn + * @property {Big} totalOut + * @property {Big} rateIn + * @property {Big} rateOut + * + * @typedef {import('.').LibP2P} LibP2P + * @typedef {import('.').PeerId} PeerId + * @typedef {import('.').CID} CID + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/stats/index.js b/packages/ipfs-core/src/components/stats/index.js new file mode 100644 index 0000000000..6760c21853 --- /dev/null +++ b/packages/ipfs-core/src/components/stats/index.js @@ -0,0 +1,29 @@ +'use strict' + +const createBW = require('./bw') +const createRepo = require('../repo/stat') +const createBitswap = require('../bitswap/stat') + +class StatsAPI { + /** + * @param {Object} config + * @param {Repo} config.repo + * @param {NetworkService} config.network + */ + constructor ({ repo, network }) { + this.repo = createRepo({ repo }) + this.bw = createBW({ network }) + this.bitswap = createBitswap({ network }) + } +} + +module.exports = StatsAPI + +/** + * @typedef {import('..').Repo} Repo + * @typedef {import('..').PeerId} PeerId + * @typedef {import('..').LibP2P} LibP2P + * @typedef {import('..').CID} CID + * @typedef {import('..').NetworkService} NetworkService + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/stop.js b/packages/ipfs-core/src/components/stop.js index 88e320aaea..d5ccc44c2c 100644 --- a/packages/ipfs-core/src/components/stop.js +++ b/packages/ipfs-core/src/components/stop.js @@ -1,232 +1,27 @@ 'use strict' -const defer = require('p-defer') -const { NotStartedError, AlreadyInitializedError } = require('../errors') -const Components = require('./') -const { withTimeoutOption } = require('../utils') +const Service = require('../utils/service') -module.exports = ({ - apiManager, - options: constructorOptions, - bitswap, - blockService, - gcLock, - initOptions, - ipld, - ipns, - keychain, - libp2p, - mfsPreload, - peerId, - pinManager = {}, - preload, - print, - repo -}) => { - /** - * Stops the IPFS node and in case of talking with an IPFS Daemon, it stops - * the process. - * - * @param {AbortOptions} _options - * @returns {Promise} - * @example - * ```js - * await ipfs.stop() - * ``` - */ - async function stop (_options) { - const stopPromise = defer() - const { cancel } = apiManager.update({ stop: () => stopPromise.promise }) - - try { - blockService.unsetExchange() - bitswap.stop() - preload.stop() - - await Promise.all([ - ipns.republisher.stop(), - mfsPreload.stop(), - libp2p.stop(), - repo.close() - ]) - - const api = createApi({ - apiManager, - constructorOptions, - blockService, - gcLock, - initOptions, - ipld, - keychain, - peerId, - pinManager, - preload, - print, - repo - }) - - apiManager.update(api, () => { throw new NotStartedError() }) - } catch (err) { - cancel() - stopPromise.reject(err) - throw err - } - - stopPromise.resolve() - } - - return withTimeoutOption(stop) -} - -function createApi ({ - apiManager, - constructorOptions, - blockService, - gcLock, - initOptions, - ipld, - keychain, - peerId, - pinManager, - preload, - print, - repo -}) { - const dag = { - get: Components.dag.get({ ipld, preload }), - resolve: Components.dag.resolve({ ipld, preload }), - tree: Components.dag.tree({ ipld, preload }), - // FIXME: resolve this circular dependency - get put () { - const put = Components.dag.put({ ipld, pin, gcLock, preload }) - Object.defineProperty(this, 'put', { value: put }) - return put - } - } - const object = { - data: Components.object.data({ ipld, preload }), - get: Components.object.get({ ipld, preload }), - links: Components.object.links({ dag }), - new: Components.object.new({ ipld, preload }), - patch: { - addLink: Components.object.patch.addLink({ ipld, gcLock, preload }), - appendData: Components.object.patch.appendData({ ipld, gcLock, preload }), - rmLink: Components.object.patch.rmLink({ ipld, gcLock, preload }), - setData: Components.object.patch.setData({ ipld, gcLock, preload }) - }, - put: Components.object.put({ ipld, gcLock, preload }), - stat: Components.object.stat({ ipld, preload }) - } - - const pinAddAll = Components.pin.addAll({ pinManager, gcLock, dag }) - const pinRmAll = Components.pin.rmAll({ pinManager, gcLock, dag }) - - const pin = { - add: Components.pin.add({ addAll: pinAddAll }), - addAll: pinAddAll, - ls: Components.pin.ls({ pinManager, dag }), - rm: Components.pin.rm({ rmAll: pinRmAll }), - rmAll: pinRmAll - } - - const block = { - get: Components.block.get({ blockService, preload }), - put: Components.block.put({ blockService, pin, gcLock, preload }), - rm: Components.block.rm({ blockService, gcLock, pinManager }), - stat: Components.block.stat({ blockService, preload }) - } - - const addAll = Components.addAll({ block, preload, pin, gcLock, options: constructorOptions }) - const resolve = Components.resolve({ ipld }) - const refs = Object.assign( - Components.refs({ ipld, resolve, preload }), - { local: Components.refs.local({ repo }) } - ) - - const notStarted = async () => { // eslint-disable-line require-await - throw new NotStartedError() - } - - const api = { - add: Components.add({ addAll }), - addAll, - bitswap: { - stat: notStarted, - unwant: notStarted, - wantlist: notStarted, - wantlistForPeer: notStarted - }, - block, - bootstrap: { - add: Components.bootstrap.add({ repo }), - clear: Components.bootstrap.clear({ repo }), - list: Components.bootstrap.list({ repo }), - reset: Components.bootstrap.reset({ repo }), - rm: Components.bootstrap.rm({ repo }) - }, - cat: Components.cat({ ipld, preload }), - config: Components.config({ repo }), - dag, - dns: Components.dns(), - files: Components.files({ ipld, block, blockService, repo, preload, options: constructorOptions }), - get: Components.get({ ipld, preload }), - id: Components.id({ peerId }), - init: async () => { // eslint-disable-line require-await - throw new AlreadyInitializedError() - }, - isOnline: Components.isOnline({}), - key: { - export: Components.key.export({ keychain }), - gen: Components.key.gen({ keychain }), - import: Components.key.import({ keychain }), - info: Components.key.info({ keychain }), - list: Components.key.list({ keychain }), - rename: Components.key.rename({ keychain }), - rm: Components.key.rm({ keychain }) - }, - ls: Components.ls({ ipld, preload }), - object, - pin, - refs, - repo: { - gc: Components.repo.gc({ gcLock, pin, refs, repo }), - stat: Components.repo.stat({ repo }), - version: Components.repo.version({ repo }) - }, - resolve, - start: Components.start({ - apiManager, - options: constructorOptions, - blockService, - gcLock, - initOptions, - ipld, - keychain, - peerId, - pinManager, - preload, - print, - repo - }), - stats: { - bitswap: notStarted, - bw: notStarted, - repo: Components.repo.stat({ repo }) - }, - stop: () => {}, - swarm: { - addrs: notStarted, - connect: notStarted, - disconnect: notStarted, - localAddrs: Components.swarm.localAddrs({ multiaddrs: [] }), - peers: notStarted - }, - version: Components.version({ repo }) +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + * @param {import('.').Preload} config.preload + * @param {import('.').BlockService} config.blockService + * @param {import('.').IPNS} config.ipns + * @param {import('.').Repo} config.repo + * @param {import('.').MFSPreload} config.mfsPreload + */ +module.exports = ({ network, preload, blockService, ipns, repo, mfsPreload }) => { + const stop = async () => { + await Promise.all([ + blockService.unsetExchange(), + preload.stop(), + ipns.stop(), + mfsPreload.stop(), + Service.stop(network), + repo.close() + ]) } - return api + return stop } - -/** - * @typedef {import('../utils').AbortOptions} AbortOptions - */ diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js new file mode 100644 index 0000000000..25c39b80e0 --- /dev/null +++ b/packages/ipfs-core/src/components/storage.js @@ -0,0 +1,295 @@ +'use strict' + +const log = require('debug')('ipfs:components:peer:storage') +const createRepo = require('../runtime/repo-nodejs') + +const { ERR_REPO_NOT_INITIALIZED } = require('ipfs-repo').errors +const uint8ArrayFromString = require('uint8arrays/from-string') +const uint8ArrayToString = require('uint8arrays/to-string') +const PeerId = require('peer-id') +const { mergeOptions } = require('../utils') +const configService = require('./config') +const { NotEnabledError } = require('../errors') +const createLibP2P = require('./libp2p') + +class Storage { + /** + * @private + * @param {PeerId} peerId + * @param {Keychain} keychain + * @param {Repo} repo + * @param {Print} print + * @param {string|undefined} pass + * @param {boolean} isInitialized + */ + constructor (peerId, keychain, repo, print, pass, isInitialized) { + this.print = print + this.peerId = peerId + this.keychain = keychain + this.repo = repo + this.print = print + this.pass = pass + this.isInitialized = isInitialized + } + + /** + * + * @param {Options} options + */ + static async start (options) { + const { repoAutoMigrate: autoMigrate, repo: repoInit, print, pass } = options + + const repo = (typeof repoInit === 'string' || repoInit == null) + ? createRepo({ path: repoInit, autoMigrate }) + : repoInit + + const { peerId, keychain, isInitialized } = await loadRepo(repo, options) + + return new Storage(peerId, keychain, repo, print, pass, isInitialized) + } +} +module.exports = Storage + +/** + * + * @param {Repo} repo + * @param {RepoOptions & InitOptions} options + * @returns {Promise<{peerId: PeerId, keychain:Keychain, isInitialized:boolean }>} + */ +const loadRepo = async (repo, options) => { + const openError = await openRepo(repo) + if (openError == null) { + // If opened succefully configure repo + return { ...await configureRepo(repo, options), isInitialized: true } + } else if (openError.code === ERR_REPO_NOT_INITIALIZED) { + if (options.allowNew === false) { + throw new NotEnabledError('new repo initialization is not enabled') + } else { + // If failed to open, because repo isn't initilaized and initalizing a + // new repo allowed, init repo: + return { ...await initRepo(repo, options), isInitialized: false } + } + } else { + throw openError + } +} + +/** + * Attempts to open given repo unless it is already open and returns result + * containing repo or an error if failed. + * + * @param {Repo} repo + * @returns {Promise<(Error & { code: number }) | null>} + */ +const openRepo = async (repo) => { + // If repo is closed attempt to open it. + if (!repo.closed) { + try { + await repo.open() + return null + } catch (error) { + return error + } + } else { + return null + } +} + +/** + * @param {Repo} repo + * @param {RepoOptions & InitOptions} options + * @returns {Promise<{peerId: PeerId, keychain:Keychain}>} + */ +const initRepo = async (repo, options) => { + // 1. Verify that repo does not exist yet (if it does and we could not + // open it we give up) + const exists = await repo.exists() + log('repo exists?', exists) + + if (exists === true) { + throw new Error('repo already exists') + } + + // 2. Restore `peerId` from a given `.privateKey` or init new using + // provide options. + const peerId = options.privateKey + ? await decodePeerId(options.privateKey) + : await initPeerId(options) + + const identity = peerIdToIdentity(peerId) + + log('peer identity: %s', identity.PeerID) + + // 3. Init new repo with provided `.config` and restored / initalized + // peerd identity. + const config = { + ...options.config, + Identity: identity + } + await repo.init(config) + + // 4. Open initalized repo. + await repo.open() + + log('repo opened') + + // Create libp2p for Keychain creation + const libp2p = createLibP2P({ + peerId, + repo, + config, + keychainConfig: { + pass: options.pass + } + }) + + if (libp2p.keychain && libp2p.keychain.opts) { + await libp2p.loadKeychain() + + await repo.config.set('Keychain', { + dek: libp2p.keychain.opts.dek + }) + } + + return { peerId, keychain: libp2p.keychain } +} + +/** + * Takes `peerId` either represented as a string serialized string or + * an instance and returns a `PeerId` instance. + * + * @param {PeerId|string} peerId + * @returns {Promise|PeerId} + */ +const decodePeerId = (peerId) => { + log('using user-supplied private-key') + return typeof peerId === 'object' + ? peerId + : PeerId.createFromPrivKey(uint8ArrayFromString(peerId, 'base64pad')) +} + +/** + * Initializes new PeerId by generting an underlying keypair. + * + * @param {Object} options + * @param {KeyType} [options.algorithm='RSA'] + * @param {number} [options.bits=2048] + * @param {Print} options.print + * @returns {Promise} + */ +const initPeerId = ({ print, algorithm = 'RSA', bits = 2048 }) => { + // Generate peer identity keypair + transform to desired format + add to config. + print('generating %s-bit (rsa only) %s keypair...', bits, algorithm) + return PeerId.create({ keyType: algorithm, bits }) +} + +/** + * @param {PeerId} peerId + */ +const peerIdToIdentity = (peerId) => ({ + PeerID: peerId.toB58String(), + /** @type {string} */ + PrivKey: uint8ArrayToString(peerId.privKey.bytes, 'base64pad') +}) + +/** + * Applies passed `profiles` and a `config` to an open repo. + * + * @param {Repo} repo + * @param {ConfigureOptions} options + * @returns {Promise<{peerId: PeerId, keychain:Keychain}>} + */ +const configureRepo = async (repo, { config, profiles, pass }) => { + const original = await repo.config.getAll() + const changed = mergeConfigs(applyProfiles(original, profiles), config) + + if (original !== changed) { + await repo.config.replace(changed) + } + + // @ts-ignore - Identity may not be present + const peerId = await PeerId.createFromPrivKey(changed.Identity.PrivKey) + const libp2p = createLibP2P({ + peerId, + repo, + config: changed, + keychainConfig: { + pass, + ...changed.Keychain + } + }) + libp2p.keychain && await libp2p.loadKeychain() + + return { peerId, keychain: libp2p.keychain } +} + +/** + * @param {IPFSConfig} config + * @param {Partial} [changes] + */ +const mergeConfigs = (config, changes) => + changes ? mergeOptions(config, changes) : config + +/** + * Apply profiles (e.g. ['server', 'lowpower']) to config + * + * @param {IPFSConfig} config + * @param {string[]} [profiles] + */ +const applyProfiles = (config, profiles) => { + return (profiles || []).reduce((config, name) => { + const profile = configService.profiles[name] + if (!profile) { + throw new Error(`Could not find profile with name '${name}'`) + } + log('applying profile %s', name) + return profile.transform(config) + }, config) +} + +/** + * @typedef {StorageOptions & RepoOptions & InitOptions} Options + * + * @typedef {Object} StorageOptions + * @property {Repo|string} [repo='~/.jsipfs'] - The file path at which to store the + * IPFS node’s data. Alternatively, you can set up a customized storage system + * by providing an Repo implementation. (In browser default is 'ipfs'). + * @property {boolean} [repoAutoMigrate=true] - js-ipfs comes bundled with a tool + * that automatically migrates your IPFS repository when a new version is + * available. + * @property {boolean} [repoOwner] + * @property {IPLDOptions} [ipld] + * + * + * @typedef {Object} RepoOptions + * @property {Print} print + * @property {IPFSConfig} [config] + * + * @typedef {Object} ConfigureOptions + * @property {IPFSConfig} [options.config] + * @property {string[]} [options.profiles] + * @property {string} [options.pass] + * + * @typedef {Object} InitOptions - On Frist run js-ipfs will initalize a repo + * which can be customized through this settings. + * @property {boolean} [emptyRepo=false] - Whether to remove built-in assets, + * like the instructional tour and empty mutable file system, from the repo. + * @property {KeyType} [algorithm='RSA'] - The type of key to use. + * @property {number} [bits=2048] - Number of bits to use in the generated key + * pair (rsa only). + * @property {PeerId|string} [privateKey] - A pre-generated private key to use. + * **NOTE: This overrides `bits`.** + * @property {string} [pass] - A passphrase to encrypt keys. You should + * generally use the top-level `pass` option instead of the `init.pass` + * option (this one will take its value from the top-level option if not set). + * @property {string[]} [profiles] - Apply profile settings to config. + * @property {boolean} [allowNew=true] - Set to `false` to disallow + * initialization if the repo does not already exist. + * + * @typedef {import('.').IPLDOptions} IPLDOptions + * @typedef {import('.').Print} Print + * @typedef {import('.').IPFSConfig} IPFSConfig + * @typedef {import('../interface/repo').Repo} Repo + * @typedef {import('libp2p-crypto').KeyType} KeyType + * @typedef {import('libp2p').LibP2PKeychain} Keychain + */ diff --git a/packages/ipfs-core/src/components/swarm/addrs.js b/packages/ipfs-core/src/components/swarm/addrs.js index cca0c83d32..657d889281 100644 --- a/packages/ipfs-core/src/components/swarm/addrs.js +++ b/packages/ipfs-core/src/components/swarm/addrs.js @@ -2,9 +2,20 @@ const { withTimeoutOption } = require('../../utils') -module.exports = ({ libp2p }) => { - return withTimeoutOption(async function addrs (options) { // eslint-disable-line require-await +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + */ +module.exports = ({ network }) => { + /** + * List of known addresses of each peer connected. + * + * @param {import('../../utils').AbortOptions} options + * @returns {Promise} + */ + async function addrs (options) { // eslint-disable-line require-await const peers = [] + const { libp2p } = await network.use(options) for (const [peerId, peer] of libp2p.peerStore.peers.entries(options)) { peers.push({ id: peerId, @@ -12,5 +23,15 @@ module.exports = ({ libp2p }) => { }) } return peers - }) + } + + return withTimeoutOption(addrs) } + +/** + * @typedef {Object} PeerInfo + * @property {string} id + * @property {Multiaddr[]} addrs + * + * @typedef {import('.').Multiaddr} Multiaddr + */ diff --git a/packages/ipfs-core/src/components/swarm/connect.js b/packages/ipfs-core/src/components/swarm/connect.js index aafa951742..541e4e4553 100644 --- a/packages/ipfs-core/src/components/swarm/connect.js +++ b/packages/ipfs-core/src/components/swarm/connect.js @@ -2,8 +2,22 @@ const { withTimeoutOption } = require('../../utils') -module.exports = ({ libp2p }) => { - return withTimeoutOption(function connect (addr, options) { +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + */ +module.exports = ({ network }) => { + /** + * Open a connection to a given address. + * + * @param {import('.').Multiaddr} addr + * @param {import('.').AbortOptions} [options] + * @returns {Promise} + */ + async function connect (addr, options) { + const { libp2p } = await network.use(options) return libp2p.dial(addr, options) - }) + } + + return withTimeoutOption(connect) } diff --git a/packages/ipfs-core/src/components/swarm/disconnect.js b/packages/ipfs-core/src/components/swarm/disconnect.js index 7d09d43edb..6c1c16bd0d 100644 --- a/packages/ipfs-core/src/components/swarm/disconnect.js +++ b/packages/ipfs-core/src/components/swarm/disconnect.js @@ -2,8 +2,22 @@ const { withTimeoutOption } = require('../../utils') -module.exports = ({ libp2p }) => { - return withTimeoutOption(function disconnect (addr, options) { +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + */ +module.exports = ({ network }) => { + /** + * Close a connection on a given address. + * + * @param {import('.').Multiaddr} addr + * @param {import('.').AbortOptions} [options] + * @returns {Promise} + */ + async function disconnect (addr, options) { + const { libp2p } = await network.use(options) return libp2p.hangUp(addr, options) - }) + } + + return withTimeoutOption(disconnect) } diff --git a/packages/ipfs-core/src/components/swarm/index.js b/packages/ipfs-core/src/components/swarm/index.js new file mode 100644 index 0000000000..29af23f83b --- /dev/null +++ b/packages/ipfs-core/src/components/swarm/index.js @@ -0,0 +1,29 @@ +'use strict' + +const createAddrsAPI = require('./addrs') +const createConnectAPI = require('./connect') +const createDisconnectAPI = require('./disconnect') +const createLocalAddrsAPI = require('./local-addrs') +const createPeersAPI = require('./peers') + +class SwarmAPI { + /** + * @param {Object} config + * @param {NetworkService} config.network + */ + constructor ({ network }) { + this.addrs = createAddrsAPI({ network }) + this.connect = createConnectAPI({ network }) + this.disconnect = createDisconnectAPI({ network }) + this.localAddrs = createLocalAddrsAPI({ network }) + this.peers = createPeersAPI({ network }) + } +} + +module.exports = SwarmAPI + +/** + * @typedef {import('..').NetworkService} NetworkService + * @typedef {import('..').Multiaddr} Multiaddr + * @typedef {import('..').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/swarm/local-addrs.js b/packages/ipfs-core/src/components/swarm/local-addrs.js index 10b7f04cc3..d679cee9c2 100644 --- a/packages/ipfs-core/src/components/swarm/local-addrs.js +++ b/packages/ipfs-core/src/components/swarm/local-addrs.js @@ -2,8 +2,25 @@ const { withTimeoutOption } = require('../../utils') -module.exports = ({ multiaddrs }) => { - return withTimeoutOption(async function localAddrs () { // eslint-disable-line require-await - return multiaddrs - }) +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + */ +module.exports = ({ network }) => { + /** + * Local addresses this node is listening on. + * + * @param {import('.').AbortOptions} [options] + * @returns {Promise} + */ + async function localAddrs (options) { + const { libp2p } = await network.use(options) + return libp2p.multiaddrs + } + + return withTimeoutOption(localAddrs) } + +/** + * @typedef {import('.').Multiaddr} Multiaddr + */ diff --git a/packages/ipfs-core/src/components/swarm/peers.js b/packages/ipfs-core/src/components/swarm/peers.js index 4a0654cad7..27377c6983 100644 --- a/packages/ipfs-core/src/components/swarm/peers.js +++ b/packages/ipfs-core/src/components/swarm/peers.js @@ -1,9 +1,19 @@ 'use strict' const { withTimeoutOption } = require('../../utils') - -module.exports = ({ libp2p }) => { - return withTimeoutOption(async function peers (options = {}) { // eslint-disable-line require-await +/** + * @param {Object} config + * @param {import('.').NetworkService} config.network + */ +module.exports = ({ network }) => { + /** + * Local addresses this node is listening on. + * + * @param {PeersOptions & AbortOptions} [options] + * @returns {Promise} + */ + async function peers (options = {}) { + const { libp2p } = await network.use(options) const verbose = options.v || options.verbose const peers = [] @@ -28,5 +38,26 @@ module.exports = ({ libp2p }) => { } return peers - }) + } + + return withTimeoutOption(peers) } + +/** + * @typedef {Object} PeerConnection + * @property {Multiaddr} addr + * @property {string} peer + * @property {string} [latency] + * @property {string} [muxer] + * @property {number} [direction] + * + * @typedef {Object} PeersOptions + * @property {boolean} [direction=false] + * @property {boolean} [streams=false] + * @property {boolean} [verbose=false] + * @property {boolean} [v=false] + * @property {boolean} [latency=false] + * + * @typedef {import('.').Multiaddr} Multiaddr + * @typedef {import('.').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/components/version.js b/packages/ipfs-core/src/components/version.js index 72b304a62f..20ab5cd9be 100644 --- a/packages/ipfs-core/src/components/version.js +++ b/packages/ipfs-core/src/components/version.js @@ -6,11 +6,15 @@ const { withTimeoutOption } = require('../utils') // gitHead is defined in published versions const meta = { gitHead: '', ...pkg } +/** + * @param {Object} config + * @param {import('.').Repo} config.repo + */ module.exports = ({ repo }) => { /** * Returns the implementation version * - * @param {import('../utils').AbortOptions} [options] + * @param {import('.').AbortOptions} [options] * @returns {Promise} * @example * ```js @@ -40,7 +44,7 @@ module.exports = ({ repo }) => { * supported by this node * * @property {string} version - * @property {string} repo + * @property {number} repo * @property {string} [commit] * @property {string} [interface-ipfs-core] * @property {string} [ipfs-http-client] diff --git a/packages/ipfs-core/src/errors.js b/packages/ipfs-core/src/errors.js index 465576b322..e4a434954a 100644 --- a/packages/ipfs-core/src/errors.js +++ b/packages/ipfs-core/src/errors.js @@ -44,6 +44,28 @@ class NotStartedError extends Error { NotStartedError.code = 'ERR_NOT_STARTED' exports.NotStartedError = NotStartedError +class AlreadyStartingError extends Error { + constructor (message = 'cannot start, already startin') { + super(message) + this.name = 'AlreadyStartingError' + this.code = AlreadyStartingError.code + } +} + +AlreadyStartingError.code = 'ERR_ALREADY_STARTING' +exports.AlreadyStartingError = AlreadyStartingError + +class AlreadyStartedError extends Error { + constructor (message = 'cannot start, already started') { + super(message) + this.name = 'AlreadyStartedError' + this.code = AlreadyStartedError.code + } +} + +AlreadyStartedError.code = 'ERR_ALREADY_STARTED' +exports.AlreadyStartedError = AlreadyStartedError + class NotEnabledError extends Error { constructor (message = 'not enabled') { super(message) diff --git a/packages/ipfs-core/src/index.js b/packages/ipfs-core/src/index.js index e718345207..bef8d923e3 100644 --- a/packages/ipfs-core/src/index.js +++ b/packages/ipfs-core/src/index.js @@ -1,10 +1,5 @@ 'use strict' -const log = require('debug')('ipfs') - -/** @type {typeof Object.assign} */ -const mergeOptions = require('merge-options') -const { isTest } = require('ipfs-utils/src/env') const globSource = require('ipfs-utils/src/files/glob-source') const urlSource = require('ipfs-utils/src/files/url-source') const PeerId = require('peer-id') @@ -16,70 +11,10 @@ const multicodec = require('multicodec') const multihashing = require('multihashing-async') const multihash = multihashing.multihash const CID = require('cids') -const { NotInitializedError } = require('./errors') -const Components = require('./components') -const ApiManager = require('./api-manager') - -const getDefaultOptions = () => ({ - init: true, - start: true, - EXPERIMENTAL: {}, - preload: { - enabled: !isTest, // preload by default, unless in test env - addresses: [ - '/dns4/node0.preload.ipfs.io/https', - '/dns4/node1.preload.ipfs.io/https', - '/dns4/node2.preload.ipfs.io/https', - '/dns4/node3.preload.ipfs.io/https' - ] - } -}) - -/** - * Creates and returns a ready to use instance of an IPFS node. - * - * @template {boolean | InitOptions} Init - * @template {boolean} Start - * @param {CreateOptions} [options] - */ -async function create (options = {}) { - options = mergeOptions(getDefaultOptions(), options) - - // eslint-disable-next-line no-console - const print = options.silent ? log : console.log - - const apiManager = new ApiManager() - - const { api } = apiManager.update({ - init: Components.init({ apiManager, print, options }), - dns: Components.dns(), - isOnline: Components.isOnline({ libp2p: undefined }) - }, async () => { throw new NotInitializedError() }) // eslint-disable-line require-await - - const initializedApi = options.init && await api.init() - const startedApi = options.start && initializedApi && await initializedApi.start() - - /** - * create returns object that has different API set based on `options.init` - * and `options.start` values. If we just return `startedApi || initializedApi || api` - * TS will infer return type to be ` typeof startedAPI || typeof initializedApi || typeof api` - * which user would in practice act like `api` with all the extra APIs as optionals. - * - * Type trickery below attempts to affect inference by explicitly telling - * what the return type is and when. - * - * @typedef {typeof api} API - * @typedef {NonNullable} InitializedAPI - * @typedef {NonNullable} StartedAPI - * @type {If, API>} - */ - // @ts-ignore - const ipfs = startedApi || initializedApi || api - return ipfs -} +const IPFS = require('./components') module.exports = { - create, + create: IPFS.create, crypto, isIPFS, CID, @@ -92,100 +27,3 @@ module.exports = { globSource, urlSource } - -/** - * @template {boolean | InitOptions} Init - * @template {boolean} Start - * - * @typedef {Object} CreateOptions - * Options argument can be used to specify advanced configuration. - * @property {RepoOption} [repo='~/.jsipfs'] - * @property {boolean} [repoAutoMigrate=true] - `js-ipfs` comes bundled with a - * tool that automatically migrates your IPFS repository when a new version is - * available. - * @property {Init} [init=true] - Perform repo initialization steps when creating - * the IPFS node. - * Note that *initializing* a repo is different from creating an instance of - * [`ipfs.Repo`](https://github.com/ipfs/js-ipfs-repo). The IPFS constructor - * sets many special properties when initializing a repo, so you should usually - * not try and call `repoInstance.init()` yourself. - * @property {Start} [start=true] - If `false`, do not automatically - * start the IPFS node. Instead, you’ll need to manually call - * [`node.start()`](https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs/docs/MODULE.md#nodestart) - * yourself. - * @property {string} [pass=null] - A passphrase to encrypt/decrypt your keys. - * @property {boolean} [silent=false] - Prevents all logging output from the - * IPFS node. (Default: `false`) - * @property {RelayOptions} [relay={ enabled: true, hop: { enabled: false, active: false } }] - * - Configure circuit relay (see the [circuit relay tutorial] - * (https://github.com/ipfs/js-ipfs/tree/master/examples/circuit-relaying) - * to learn more). - * @property {boolean} [offline=false] - Run ipfs node offline. The node does - * not connect to the rest of the network but provides a local API. - * @property {PreloadOptions} [preload] - Configure remote preload nodes. - * The remote will preload content added on this node, and also attempt to - * preload objects requested by this node. - * @property {ExperimentalOptions} [EXPERIMENTAL] - Enable and configure - * experimental features. - * @property {object} [config] - Modify the default IPFS node config. This - * object will be *merged* with the default config; it will not replace it. - * (Default: [`config-nodejs.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/config-nodejs.js) - * in Node.js, [`config-browser.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/config-browser.js) - * in browsers) - * @property {import('./components').IPLDConfig} [ipld] - Modify the default IPLD config. This object - * will be *merged* with the default config; it will not replace it. Check IPLD - * [docs](https://github.com/ipld/js-ipld#ipld-constructor) for more information - * on the available options. (Default: [`ipld.js`] - * (https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld.js) - * in browsers) - * @property {object|Function} [libp2p] - The libp2p option allows you to build - * your libp2p node by configuration, or via a bundle function. If you are - * looking to just modify the below options, using the object format is the - * quickest way to get the default features of libp2p. If you need to create a - * more customized libp2p node, such as with custom transports or peer/content - * routers that need some of the ipfs data on startup, a custom bundle is a - * great way to achieve this. - * - You can see the bundle in action in the [custom libp2p example](https://github.com/ipfs/js-ipfs/tree/master/examples/custom-libp2p). - * - Please see [libp2p/docs/CONFIGURATION.md](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md) - * for the list of options libp2p supports. - * - Default: [`libp2p-nodejs.js`](../src/core/runtime/libp2p-nodejs.js) - * in Node.js, [`libp2p-browser.js`](../src/core/runtime/libp2p-browser.js) in - * browsers. - */ - -/** - * @typedef {IPFSRepo|string} RepoOption - * The file path at which to store the IPFS node’s data. Alternatively, you - * can set up a customized storage system by providing an `ipfs.Repo` instance. - * - * @example - * ```js - * // Store data outside your user directory - * const node = await IPFS.create({ repo: '/var/ipfs/data' }) - * ``` - * @typedef {import('./components/init').InitOptions} InitOptions - * - * @typedef {object} RelayOptions - * @property {boolean} [enabled] - Enable circuit relay dialer and listener. (Default: `true`) - * @property {object} [hop] - * @property {boolean} [hop.enabled] - Make this node a relay (other nodes can connect *through* it). (Default: `false`) - * @property {boolean} [hop.active] - Make this an *active* relay node. Active relay nodes will attempt to dial a destination peer even if that peer is not yet connected to the relay. (Default: `false`) - * - * @typedef {object} PreloadOptions - * @property {boolean} [enabled] - Enable content preloading (Default: `true`) - * @property {string[]} [addresses] - Multiaddr API addresses of nodes that should preload content. - * **NOTE:** nodes specified here should also be added to your node's bootstrap address list at `config.Boostrap`. - * - * @typedef {object} ExperimentalOptions - * @property {boolean} [ipnsPubsub] - Enable pub-sub on IPNS. (Default: `false`) - * @property {boolean} [sharding] - Enable directory sharding. Directories that have many child objects will be represented by multiple DAG nodes instead of just one. It can improve lookup performance when a directory has several thousand files or more. (Default: `false`) - * - * @typedef {import('./components').IPFSRepo} IPFSRepo - */ - -/** - * Utility type to write type level conditionals - * - * @template Conditon, Then, Else - * @typedef {NonNullable extends false ? Else : Then } If - */ diff --git a/packages/ipfs-core/src/interface/basic.ts b/packages/ipfs-core/src/interface/basic.ts new file mode 100644 index 0000000000..41dea96426 --- /dev/null +++ b/packages/ipfs-core/src/interface/basic.ts @@ -0,0 +1,28 @@ +import CID from 'cids' +import PeerId from 'peer-id' +import BigInteger from 'bignumber.js' +export type Await = + | T + | Promise + +export type AwaitIterable = + | Iterable + | AsyncIterable + +export interface AbortOptions { + signal?: AbortSignal +} +export type ToJSON = + | null + | string + | number + | boolean + | ToJSON[] + | { toJSON?: () => ToJSON } & {[key:string]: ToJSON} + +export interface Block { + cid: CID + data: Uint8Array +} + +export type { CID, PeerId, BigInteger } diff --git a/packages/ipfs-core/src/interface/bitswap.ts b/packages/ipfs-core/src/interface/bitswap.ts new file mode 100644 index 0000000000..456a232adc --- /dev/null +++ b/packages/ipfs-core/src/interface/bitswap.ts @@ -0,0 +1,88 @@ +import { PeerId, CID, Block, Await, BigInteger, AbortOptions } from './basic' +import { MovingAverage } from './moving-avarage' +import { StoreReader, StoreExporter, StoreImporter } from './store' + +export interface Bitswap extends + StoreReader, + StoreExporter, + StoreImporter +{ + + readonly peerId: PeerId + + enableStats(): void + disableStats(): void + + wantlistForPeer(peerId: PeerId, options?:AbortOptions): Map + ledgerForPeer(peerId: PeerId): Ledger + + put(block: Block, options?: AbortOptions): Await + + unwant(cids: Iterable, options?: AbortOptions): void + cancelWants(cids: Iterable): void + getWantlist(options?: AbortOptions): Iterable<[string, WantListEntry]> + peers(): PeerId[] + stat(): Stats + start(): void + stop(): void +} + +export interface Ledger { + sentBytes(n:number):void + receivedBytes(n:number):void + + wants(cid: CID, priority: number, wantType: WantType):void + cancelWant(cid: CID): void + wantlistContains(cid:CID): WantListEntry|void + + debtRatio():number +} + +export interface WantListEntry { + readonly cid: CID + priority: number + inc(): void + dec(): void + hasRefs(): boolean + equals(other: WantListEntry): boolean +} + +export type WantList = { + entries: Entry[] + full?: boolean +} + +export type Entry = { + block: Uint8Array + priority: number + cancel: boolean + wantType?: WantType + sendDontHave?: boolean +} + +export type BlockPresence = { + cid: Uint8Array + type: BlockPresenceType +} + +export type Have = 0 +export type DontHave = 1 +export type BlockPresenceType = Have | DontHave + +export type WantBlock = 0 +export type HaveBlock = 1 +export type WantType = WantBlock | HaveBlock + +export type BlockData = { + prefix: Uint8Array + data: Uint8Array +} + +export interface Stats { + enable(): void + disable(): void + stop(): void + readonly snapshot: Record + readonly movingAverages: Record> + push(counter: number, inc: number): void +} diff --git a/packages/ipfs-core/src/interface/block-service.ts b/packages/ipfs-core/src/interface/block-service.ts new file mode 100644 index 0000000000..aa206edc4d --- /dev/null +++ b/packages/ipfs-core/src/interface/block-service.ts @@ -0,0 +1,20 @@ +import { Block, CID, Await, AbortOptions } from './basic' +import { StoreReader, StoreImporter, StoreExporter, StoreEraser } from './store' +import { Bitswap } from './bitswap' + +export interface BlockService extends + StoreReader, + StoreExporter, + StoreImporter, + StoreEraser +{ + setExchange(bitswap: Bitswap): void + + unsetExchange(): void + hasExchange(): boolean + + /** + * Put a block to the underlying datastore. + */ + put(block: Block, options?:AbortOptions): Await +} diff --git a/packages/ipfs-core/src/interface/datastore.ts b/packages/ipfs-core/src/interface/datastore.ts new file mode 100644 index 0000000000..418eadccf5 --- /dev/null +++ b/packages/ipfs-core/src/interface/datastore.ts @@ -0,0 +1,185 @@ +import { KeyValueStore, StoreBatch, StoreSelector, Resource } from './store' +export interface DataStore extends + KeyValueStore, + StoreSelector, + StoreBatch, + Resource +{ +} + +export interface Key { + /** + * Returns the "name" of this key (field of last namespace). + * + * @example + * ```js + * key.toString() + * // '/Comedy/MontyPython/Actor:JohnCleese' + * key.name() + * // 'JohnCleese' + * ``` + */ + name(): string + + /** + * Returns the "type" of this key (value of last namespace). + * + * @example + * ```js + * key.toString() + * '/Comedy/MontyPython/Actor:JohnCleese' + * key.type() + * // 'Actor' + * ``` + */ + type(): string + + /** + * Returns the `namespaces` making up this `Key`. + */ + namespaces(): string[] + + /** + * Returns the "base" namespace of this key. + * + * @example + * ```js + * key.toString() + * // '/Comedy/MontyPython/Actor:JohnCleese' + * key.baseNamespace() + * // 'Actor:JohnCleese' + */ + baseNamespace(): string + + /** + * Returns an "instance" of this type key (appends value to namespace). + * + * @example + * ```js + * key.toString() + * // '/Comedy/MontyPython/Actor' + * key.instance('JohnClesse').toString() + * // '/Comedy/MontyPython/Actor:JohnCleese' + * ``` + */ + instance(): Key + + /** + * Returns the "path" of this key (parent + type). + * + * @example + * ```js + * key.toString() + * '/Comedy/MontyPython/Actor:JohnCleese' + * key.path().toString() + * // '/Comedy/MontyPython/Actor' + * ``` + */ + path(): Key + + /** + * Returns the `parent` Key of this Key. + * + * @example + * ```js + * key.toString() + * "/Comedy/MontyPython/Actor:JohnCleese" + * key.parent().toString() + * // "/Comedy/MontyPython" + * ``` + */ + parent(): Key + + /** + * Returns the `child` Key of this Key. + * + * @example + * ```js + * key.toString() + * '/Comedy/MontyPython' + * child.toString() + * // 'Actor:JohnCleese' + * key.child(child).toString() + * '/Comedy/MontyPython/Actor:JohnCleese' + * ``` + */ + child(key: Key): Key + + /** + * Check if the given key is sorted lower than this. + */ + less(key: Key): boolean + + /** + * Returns whether this key is a prefix of `other` + * + * @example + * ```js + * comedy.toString() + * '/Comedy' + * monty.toString() + * '/Comedy/MontyPython' + * comedy.isAncestorOf(monty) + * // true + * ``` + */ + isAncestorOf(other: Key): boolean + + /** + * Returns whether this key is a contains `other` as prefix. + * ```js + * comedy.toString() + * '/Comedy' + * monty.toString() + * '/Comedy/MontyPython' + * monty.isDecendantOf(comedy) + * // true + * ``` + */ + isDecendantOf(other: Key): boolean + + /** + * Returns wether this key has only one namespace. + */ + isTopLevel(): boolean + + /** + * Returns the key with all parts in reversed order. + * + * @example + * ```js + * key.toString() + * // '/Comedy/MontyPython/Actor:JohnCleese' + * key.reverse().toString() + * // /Actor:JohnCleese/MontyPython/Comedy + * new Key('/Comedy/MontyPython/Actor:JohnCleese').reverse() + * ``` + */ + reverse(): Key + + /** + * Concats one or more Keys into one new Key. + */ + concat(...keys: Key[]): Key + + /** + * Returns the array representation of this key. + * + * @example + * ```js + * key.toString() + * // '/Comedy/MontyPython/Actor:JohnCleese' + * key.list() + * // ['Comedy', 'MontyPythong', 'Actor:JohnCleese'] + * ``` + */ + list(): string[] + toString(): string +} + +export type Value = Uint8Array + +export interface Entry { + key: Key, + value: Value +} diff --git a/packages/ipfs-core/src/interface/format.ts b/packages/ipfs-core/src/interface/format.ts new file mode 100644 index 0000000000..4c0da87482 --- /dev/null +++ b/packages/ipfs-core/src/interface/format.ts @@ -0,0 +1,41 @@ + +import { Await, CID } from './basic' + +export interface Format { + util: Util + resolver: Resolver + + defaultHashArg: string | number + codec: string | number +} + +export interface Util { + /** + * Serialize an IPLD Node into a binary blob. + */ + serialize(node:T):Uint8Array + /** + * Deserialize a binary blob into an IPLD Node. + */ + deserialize(bytes: Uint8Array): T + + /** + * Calculate the CID of the binary blob. + */ + cid(bytes:Uint8Array, options?:CIDOptions): Await +} + +export interface CIDOptions { + cidVersion?: number + hashAlg?: number | string +} + +export interface Resolver { + resolve(bytes: Uint8Array, path: string): ResolveResult + tree(byte: Uint8Array): string[] +} + +export interface ResolveResult { + value: T + remainderPath: string +} diff --git a/packages/ipfs-core/src/interface/ipld.ts b/packages/ipfs-core/src/interface/ipld.ts new file mode 100644 index 0000000000..c168e38f61 --- /dev/null +++ b/packages/ipfs-core/src/interface/ipld.ts @@ -0,0 +1,43 @@ +import { BlockService } from './block-service' +import { Await, CID, AwaitIterable, AbortOptions } from './basic' +import { StoreReader, StoreExporter, StoreEraser } from './store' +import { ResolveResult, Format } from './format' + +export interface IPLD extends + StoreReader, + StoreExporter, + StoreEraser + +{ + put(value:T, format:FormatCode, options?:PutOptions & AbortOptions):Await + putMany(values: AwaitIterable, format: FormatCode, options?:PutOptions):AwaitIterable + + resolve(cid: CID, path: string, options?: AbortOptions): AwaitIterable> + tree(cid:CID, path?:string, options?:TreeOptions & AbortOptions):AwaitIterable + + addFormat(format:Format):IPLD + removeFormat(format:Format):IPLD + + defaultOptions: Options +} + +export type FormatCode = number +export type HashAlg = number + +export interface Options { + blockService?: BlockService + formats?: Record + + loadFormat?: (code:number|string) => Promise> +} + +export interface PutOptions { + hashAlg?: HashAlg, + cidVersion?: 0|1, + onlyHash?: boolean, + +} + +export interface TreeOptions { + recursive?: boolean +} diff --git a/packages/ipfs-core/src/interface/moving-avarage.ts b/packages/ipfs-core/src/interface/moving-avarage.ts new file mode 100644 index 0000000000..99045ae3f8 --- /dev/null +++ b/packages/ipfs-core/src/interface/moving-avarage.ts @@ -0,0 +1,9 @@ +export interface MovingAverage { + variance(): number + movingAverage(): number + + deviation(): number + forecast(): number + + push(time: number, value:number):void +} diff --git a/packages/ipfs-core/src/interface/repo.ts b/packages/ipfs-core/src/interface/repo.ts new file mode 100644 index 0000000000..efe003516a --- /dev/null +++ b/packages/ipfs-core/src/interface/repo.ts @@ -0,0 +1,104 @@ +import { CID, Block, ToJSON, Await, AbortOptions } from './basic' +import { DataStore, Key } from './datastore' +import { + ValueStore, StoreReader, Resource, StoreLookup, + StoreImporter, StoreExporter, StoreEraser, StoreSelector, + KeyValueStore +} from './store' + +export interface Repo extends Resource { + readonly path: string + closed: boolean + + /** + * Initializes necessary structures inside the repo + */ + init(config:Partial):Await + + /** + * Tells whether this repo exists or not. + */ + exists():Await + + /** + * Tells whether the repo has been initialized. + */ + isInitialized():Await + + /** + * Gets the repo status. + */ + stat(options?:AbortOptions):Await + + root: KeyValueStore + + blocks: BlockStore + datastore: DataStore + + pins: PinStore + config: ConfigStore + keys: KeyStore + + version: ValueStore + apiAddr: ValueStore +} + +export interface RepoStatus { + numObjects: number + repoPath: string + repoSize: number + version: number + storageMax: number +} + +interface BlockStore extends + StoreImporter, + StoreReader, + StoreLookup, + StoreExporter, + StoreEraser, + StoreSelector +{ + put(block: Block): Await +} + +export interface ConfigStore extends + StoreReader +{ + /** + * Set a config `value`, where `value` can be anything that is serializable + * to JSON. + */ + set(key: string, value: ToJSON, options?:AbortOptions): Await + + /** + * Set the whole `config` which can be a any value that is serializable to + * JSON. + * + * @param config + */ + replace(config: Config, options?: AbortOptions): Await + + /** + * Get the entire config value. + */ + getAll(options?:AbortOptions): Await + + /** + * Whether the config sub-repo exists. + */ + exists(): Await +} + +export interface PinStore extends + KeyValueStore, + Object +{ +} + +export interface KeyStore extends + KeyValueStore, + Object +{ + +} diff --git a/packages/ipfs-core/src/interface/store.ts b/packages/ipfs-core/src/interface/store.ts new file mode 100644 index 0000000000..332c573062 --- /dev/null +++ b/packages/ipfs-core/src/interface/store.ts @@ -0,0 +1,124 @@ + +import { Await, AwaitIterable, AbortOptions } from './basic' + +export interface ValueStore { + get(options?:AbortOptions): Await + set(value: T): Await +} + +export interface KeyValueStore extends + StoreReader, + StoreExporter, + StoreSelector, + StoreLookup, + StoreWriter, + StoreImporter, + StoreEraser { +} + +// Interface Datastore + +export interface StoreReader { + /** + * The key retrieve the value for. + */ + get(key: Key, options?: AbortOptions): Await +} + +export interface StoreLookup { + /** + * Check for the existence of a given key + */ + has(key: Key, options?: AbortOptions): Await +} + +export interface StoreExporter { + /** + * Retrieve a stream of values stored under the given keys. + */ + getMany(keys: AwaitIterable, options?: AbortOptions): AwaitIterable +} + +export interface StoreSelector { + /** + * Search the store for some values. + */ + query(query: Query, options?: AbortOptions): AwaitIterable +} + +export interface StoreWriter { + /** + * Store a value with the given key. + */ + put(key: Key, value: Value, options?: AbortOptions): Await +} + +export interface StoreImporter { + /** + * Store many key-value pairs. + */ + putMany(entries: AwaitIterable, options?: AbortOptions): AwaitIterable +} + +export interface StoreEraser { + /** + * Delete the content stored under the given key. + */ + delete(key: Key, options?: AbortOptions): Await + /** + * Delete the content stored under the given keys. + */ + deleteMany(keys: AwaitIterable, options?: AbortOptions): AwaitIterable + +} +export interface StoreBatch { + batch(): Batch +} + +export interface Batch { + put(key: Key, value: Value): void + delete(key: Key): void + + commit(options?: AbortOptions): Await +} + +export interface Resource { + /** + * Opens the datastore, this is only needed if the store was closed before, + * otherwise this is taken care of by the constructor. + */ + open(): Await + /** + * Close the datastore, this should always be called to ensure resources + * are cleaned up. + */ + close(): Await +} + +export interface Query { + /** + * Only return values where the key starts with this prefix + */ + prefix?: string + /** + * Filter the results according to the these functions + */ + filters?: Array<(resut: Entry) => boolean> + /** + * Order the results according to these functions + */ + orders?: Array<(results: Entry[]) => Entry[]> + /** + * Only return this many records + */ + limit?: number + /** + * An options object, all properties are optional + */ + options?: Options + /** + * A way to signal that the caller is no longer interested in the outcome of + * this operation + */ + signal?: AbortSignal +} diff --git a/packages/ipfs-core/src/mfs-preload.js b/packages/ipfs-core/src/mfs-preload.js index aabffee3bb..1716e1206c 100644 --- a/packages/ipfs-core/src/mfs-preload.js +++ b/packages/ipfs-core/src/mfs-preload.js @@ -8,9 +8,9 @@ const log = Object.assign(debug('ipfs:mfs-preload'), { /** * @param {Object} config - * @param {import('./components/index').Preload} config.preload - * @param {import('./components/index').Files} config.files - * @param {import('./components/init').PreloadOptions} [config.options] + * @param {import('./components').Preload} config.preload + * @param {import('./components').Files} config.files + * @param {import('./components').PreloadOptions} [config.options] */ module.exports = ({ preload, files, options = {} }) => { options.interval = options.interval || 30 * 1000 diff --git a/packages/ipfs-core/src/preload.js b/packages/ipfs-core/src/preload.js index 53425347d3..6168e6ff42 100644 --- a/packages/ipfs-core/src/preload.js +++ b/packages/ipfs-core/src/preload.js @@ -6,9 +6,6 @@ const CID = require('cids') const shuffle = require('array-shuffle') const AbortController = require('native-abort-controller') const preload = require('./runtime/preload-nodejs') -/** @type {typeof import('hashlru').default} */ -// @ts-ignore - hashlru has incorrect typedefs -const hashlru = require('hashlru') const log = Object.assign( debug('ipfs:preload'), @@ -16,15 +13,11 @@ const log = Object.assign( ) /** - * @param {Object} [options] - * @param {boolean} [options.enabled = false] - Whether to preload anything - * @param {string[]} [options.addresses = []] - Which preload servers to use - * @param {number} [options.cache = 1000] - How many CIDs to cache + * @param {Options & AbortOptions} [options] */ const createPreloader = (options = {}) => { options.enabled = Boolean(options.enabled) options.addresses = options.addresses || [] - options.cache = options.cache || 1000 if (!options.enabled || !options.addresses.length) { log('preload disabled') @@ -39,9 +32,6 @@ const createPreloader = (options = {}) => { let requests = [] const apiUris = options.addresses.map(toUri) - // Avoid preloading the same CID over and over again - const cache = hashlru(options.cache) - /** * @param {string|CID} path * @returns {Promise} @@ -54,14 +44,6 @@ const createPreloader = (options = {}) => { path = new CID(path).toString() } - if (cache.has(path)) { - // we've preloaded this recently, don't preload it again - return - } - - // make sure we don't preload this again any time soon - cache.set(path, true) - const fallbackApiUris = shuffle(apiUris) let success = false const now = Date.now() @@ -111,3 +93,15 @@ const createPreloader = (options = {}) => { } module.exports = createPreloader + +/** + * @typedef {ReturnType} Preload + * + * @typedef {object} Options + * @property {boolean} [enabled] - Enable content preloading (Default: `true`) + * @property {number} [interval] + * @property {string[]} [addresses] - Multiaddr API addresses of nodes that should preload content. + * **NOTE:** nodes specified here should also be added to your node's bootstrap address list at `config.Boostrap`. + * + * @typedef {import('./components').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-core/src/runtime/repo-nodejs.js b/packages/ipfs-core/src/runtime/repo-nodejs.js index e8ad5ee54c..a5f3ec99d8 100644 --- a/packages/ipfs-core/src/runtime/repo-nodejs.js +++ b/packages/ipfs-core/src/runtime/repo-nodejs.js @@ -9,6 +9,7 @@ const path = require('path') * @param {string} [options.path] * @param {boolean} [options.silent] * @param {boolean} [options.autoMigrate] + * @returns {Repo} */ module.exports = (options = {}) => { const repoPath = options.path || path.join(os.homedir(), '.jsipfs') @@ -29,3 +30,8 @@ module.exports = (options = {}) => { onMigrationProgress: options.silent ? null : onMigrationProgress }) } + +/** + * @typedef {import('../interface/repo').Repo} Repo + * @typedef {import('../components/config').IPFSConfig} IPFSConfig + */ diff --git a/packages/ipfs-core/src/utils.js b/packages/ipfs-core/src/utils.js index 18fcad2326..3c81077388 100644 --- a/packages/ipfs-core/src/utils.js +++ b/packages/ipfs-core/src/utils.js @@ -10,10 +10,20 @@ const Key = require('interface-datastore').Key const { TimeoutError } = require('./errors') const errCode = require('err-code') const toCidAndPath = require('ipfs-core-utils/src/to-cid-and-path') +/** @type {typeof Object.assign} */ +const mergeOptions = require('merge-options') + +exports.mergeOptions = mergeOptions const ERR_BAD_PATH = 'ERR_BAD_PATH' exports.OFFLINE_ERROR = 'This command must be run in online mode. Try running \'ipfs daemon\' first.' + +exports.MFS_FILE_TYPES = { + file: 0, + directory: 1, + 'hamt-sharded-directory': 1 +} exports.MFS_ROOT_KEY = new Key('/local/filesroot') exports.MFS_MAX_CHUNK_SIZE = 262144 exports.MFS_MAX_LINKS = 174 @@ -67,7 +77,7 @@ const normalizeCidPath = (path) => { * - /ipfs//link/to/pluto * - multihash Buffer * - * @param {import('./components').DAG} dag - The IPFS dag api + * @param {import('./components').DagReader} dag * @param {CID | string} ipfsPath - A CID or IPFS path * @param {Object} [options] - Optional options passed directly to dag.resolve * @returns {Promise} @@ -314,8 +324,19 @@ function withTimeoutOption (fn, optionsArgIndex) { } } +const withTimeout = withTimeoutOption( + /** + * @template T + * @param {Promise|T} promise + * @param {AbortOptions} [_options] + * @returns {Promise} + */ + async (promise, _options) => await promise +) + exports.normalizePath = normalizePath exports.normalizeCidPath = normalizeCidPath exports.resolvePath = resolvePath exports.mapFile = mapFile exports.withTimeoutOption = withTimeoutOption +exports.withTimeout = withTimeout diff --git a/packages/ipfs-core/src/utils/service.js b/packages/ipfs-core/src/utils/service.js new file mode 100644 index 0000000000..a8eea7ab4e --- /dev/null +++ b/packages/ipfs-core/src/utils/service.js @@ -0,0 +1,239 @@ +'use strict' + +const { NotStartedError, AlreadyStartingError, AlreadyStartedError } = require('../errors') +const { withTimeout } = require('../utils') + +/** + * @template Options, T + * + * Allows you to create a handle to service that can be started or + * stopped. It enables defining components that need to use service + * functionality before service is started. + * + */ +class Service { + /** + * Takes `activation` function that takes `options` and (async) returns + * an implementation. + * + * @template {(options:any) => Await} T + * + * @param {Object} config + * @param {T} config.start + * @param {(state:State) => Await} [config.stop] + * @returns {Service[0], State>} + */ + static create ({ start, stop }) { + return new Service(start, stop) + } + + /** + * Starts the service (by running actiavtion function). Will (async) throw + * unless service is stopped. + * + * @template Options, T + * @param {Service} service + * @param {Options} options + * @returns {Promise} + */ + static async start (service, options) { + const { state, activate } = service + switch (state.status) { + // If service is in 'stopped' state we activate and transition to + // to 'pending' state. Once activation is complete transition state to + // 'started' state. + // Note: This is the only code that does state transitions from + // - stopped + // - started + // Which ensures no race conditions can occur. + case 'stopped': { + try { + const promise = activate(options) + service.state = { status: 'starting', ready: promise } + // Note: MUST await after state transition above otherwise race + // condition may occur. + const result = await promise + service.state = { status: 'started', value: result } + return result + // If failed to start, transiton from 'starting' to 'stopped' + // state. + } catch (error) { + service.state = { status: 'stopped' } + throw error + } + } + case 'starting': { + throw new AlreadyStartingError() + } + case 'started': { + throw new AlreadyStartedError() + } + // If service is stopping we just wait for that to complete + // and try again. + case 'stopping': { + await state.ready + return await Service.start(service, options) + } + default: { + return Service.panic(service) + } + } + } + + /** + * Stops the service by executing deactivation. If service is stopped + * or is stopping this is noop. If service is starting up when called + * it will await for start to complete and then retry stop afterwards. + * This may (async) throw if `deactivate` does. + * + * @template T + * @param {Service} service + * @returns {Promise} + */ + static async stop (service) { + const { state, deactivate } = service + switch (state.status) { + // If stopped there's nothing to do. + case 'stopped': { + break + } + // If service is starting we await for it to complete + // and try again. That way + case 'starting': { + // We do not want to error stop if start failed. + try { await state.ready } catch (_) {} + return await Service.stop(service) + } + // if service is stopping we just await for it to complete. + case 'stopping': { + return await state.ready + } + case 'started': { + if (deactivate) { + await deactivate(state.value) + } + service.state = { status: 'stopped' } + break + } + default: { + Service.panic(state) + } + } + } + + /** + * @template T + * @param {Service} service + * @returns {T|null} + */ + static try ({ state }) { + switch (state.status) { + case 'started': + return state.value + default: + return null + } + } + + /** + * Unwraps state and returns underlying value. If state is in idle state it + * will throw an error. If state is pending it will wait and return the + * result or throw on failure. If state is ready returns result. + * + * @template T + * @param {Service} service + * @param {AbortOptions} [options] + * @returns {Promise} + */ + static async use ({ state }, options) { + switch (state.status) { + case 'started': + return state.value + case 'starting': + return await withTimeout(state.ready, options) + default: + throw new NotStartedError() + } + } + + // eslint-disable-next-line jsdoc/require-returns-check + /** + * @private + * @param {Service} service + * @returns {never} + */ + static panic ({ state }) { + const status = JSON.stringify({ status: state.status }) + throw RangeError(`Service in invalid state ${status}, should never happen if you see this please report a bug`) + } + + /** + * Takes `activation` function that takes `options` and (async) returns + * an implementation. + * + * @private + * @param {(options:Options) => Await} activate + * @param {(state:T) => Await} [deactivate] + */ + constructor (activate, deactivate) { + this.activate = activate + this.deactivate = deactivate + + /** + * A state machine for this service. + * + * @private + * @type {ServiceState} + */ + this.state = { status: 'stopped' } + } + + /** + * Allows you to asynchronously obtain service implementation. If service + * is starting it will await for completion. If service is stopped or stopping + * this will (async) throw exception. This allows components that need to use + * this service convinient API to do it. + * + * @param {AbortOptions} [options] - Abort options. + * @returns {Promise} + */ + async use (options) { + return await Service.use(this, options) + } + + /** + * @returns {T|null} + */ + try () { + return Service.try(this) + } +} +module.exports = Service + +/** + * @template T + * @typedef {import('../interface/basic').Await} Await + */ +/** + * @template {(options:any) => any} T + * @typedef {Parameters[0]} Options + */ +/** + * @template {(options:any) => any} T + * @typedef {ReturnType extends ? Promise ? U : ReturnType} State + */ +/** + * Reperests service state which can be not started in which case + * it is instaceof `Error`. Pending in which case it's promise or + * ready in which case it is the value itself. + * + * @template T + * @typedef {{ status: 'stopped' } + * | { status: 'starting', ready: Await } + * | { status: 'started', value: T } + * | { status: 'stopping', ready: Await } + * } ServiceState + */ +/** + * @typedef {import('../utils').AbortOptions} AbortOptions + */ diff --git a/packages/ipfs-http-client/src/block/put.js b/packages/ipfs-http-client/src/block/put.js index d64b747ff9..36be441477 100644 --- a/packages/ipfs-http-client/src/block/put.js +++ b/packages/ipfs-http-client/src/block/put.js @@ -23,6 +23,8 @@ module.exports = configure(api => { mhlen: length, version: data.cid.version } + // @ts-ignore - data is typed as block so TS complains about + // Uint8Array assignment. data = data.data } else if (options.cid) { const cid = new CID(options.cid) diff --git a/packages/ipfs-http-client/src/dag/put.js b/packages/ipfs-http-client/src/dag/put.js index 2a22176591..e235edac82 100644 --- a/packages/ipfs-http-client/src/dag/put.js +++ b/packages/ipfs-http-client/src/dag/put.js @@ -23,21 +23,24 @@ module.exports = configure((api, opts) => { throw new Error('Failed to put DAG node. Provide `format` AND `hashAlg` options') } + let encodingOptions if (options.cid) { const cid = new CID(options.cid) - options = { + encodingOptions = { ...options, format: multicodec.getName(cid.code), hashAlg: multihash.decode(cid.multihash).name } delete options.cid + } else { + encodingOptions = options } const settings = { format: 'dag-cbor', hashAlg: 'sha2-256', inputEnc: 'raw', - ...options + ...encodingOptions } const format = await load(settings.format) From 8e1d141841ac235bd6f5f13fc11676c8b6509177 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sat, 31 Oct 2020 12:04:01 -0700 Subject: [PATCH 02/29] fix: initalization regressions --- packages/ipfs-core/src/components/config.js | 4 +++ packages/ipfs-core/src/components/index.js | 10 ++++-- packages/ipfs-core/src/components/ipns.js | 4 ++- packages/ipfs-core/src/components/start.js | 11 +++++-- packages/ipfs-core/src/components/stop.js | 2 +- packages/ipfs-core/src/components/storage.js | 6 ++-- .../ipfs-core/src/runtime/config-nodejs.js | 3 ++ packages/ipfs-core/test/create-node.spec.js | 3 +- packages/ipfs-core/test/init.spec.js | 32 ++++++++++--------- 9 files changed, 48 insertions(+), 27 deletions(-) diff --git a/packages/ipfs-core/src/components/config.js b/packages/ipfs-core/src/components/config.js index 7584eb3bdf..ab585a132e 100644 --- a/packages/ipfs-core/src/components/config.js +++ b/packages/ipfs-core/src/components/config.js @@ -311,6 +311,7 @@ module.exports.profiles = profiles * @property {KeychainConfig} [Keychain] * @property {PubsubConfig} [Pubsub] * @property {SwarmConfig} [Swarm] + * @property {RoutingConfig} [Routing] * * @typedef {Object} AddressConfig * Contains information about various listener addresses to be used by this node. @@ -501,6 +502,9 @@ module.exports.profiles = profiles * * {{LowWater?:number, HighWater?:number}} ConnMgr * + * @typedef {Object} RoutingConfig + * @property {string} [Type] + * * @typedef {import('../interface/basic').ToJSON} ToJSON * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index b52fbc54a4..3fee9bffb1 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -8,6 +8,7 @@ const { DAGNode } = require('ipld-dag-pb') const UnixFs = require('ipfs-unixfs') const multicodec = require('multicodec') const initAssets = require('../runtime/init-assets-nodejs') +const { AlreadyInitializedError } = require('../errors') const createStartAPI = require('./start') const createStopAPI = require('./stop') @@ -176,8 +177,9 @@ class IPFS { }) } - async init () { + async init () { // eslint-disable-line require-await // Just keep this around for backwards compatibility + throw new AlreadyInitializedError() } /** @@ -189,6 +191,10 @@ class IPFS { // eslint-disable-next-line no-console const print = options.silent ? log : console.log + if (options.init === false) { + throw new Error('Creating a non-initalized repo is no longer supported') + } + const init = { ...mergeOptions(initOptions(options), options), print @@ -231,7 +237,7 @@ module.exports = IPFS * @returns {InitOptions} */ const initOptions = ({ init }) => - init === 'object' ? init : {} + typeof init === 'object' ? init : {} /** * @param {IPFS} ipfs diff --git a/packages/ipfs-core/src/components/ipns.js b/packages/ipfs-core/src/components/ipns.js index 0f26670896..eb9d255912 100644 --- a/packages/ipfs-core/src/components/ipns.js +++ b/packages/ipfs-core/src/components/ipns.js @@ -65,13 +65,14 @@ class IPNSAPI { * @param {import('.').PeerId} config.peerId * @param {import('.').Keychain} config.keychain */ - startOnline ({ libp2p, repo, peerId, keychain }) { + async startOnline ({ libp2p, repo, peerId, keychain }) { if (this.online != null) { throw new AlreadyInitializedError() } const routing = routingConfig({ libp2p, repo, peerId, options: this.options }) const ipns = new IPNS(routing, repo.datastore, peerId, keychain, this.options) + await ipns.republisher.start() this.online = ipns } @@ -79,6 +80,7 @@ class IPNSAPI { const ipns = this.online if (ipns) { await ipns.republisher.stop() + this.online = null } } diff --git a/packages/ipfs-core/src/components/start.js b/packages/ipfs-core/src/components/start.js index 63b6378443..32d0862a5c 100644 --- a/packages/ipfs-core/src/components/start.js +++ b/packages/ipfs-core/src/components/start.js @@ -15,7 +15,7 @@ const Service = require('../utils/service') * @param {import('.').Keychain} config.keychain * @param {string} [config.pass] */ -module.exports = ({ network, preload, peerId, keychain, repo, ipns, blockService, print, pass }) => { +module.exports = ({ network, preload, peerId, keychain, repo, ipns, blockService, mfsPreload, print, pass }) => { const start = async () => { const { bitswap, libp2p } = await Service.start(network, { peerId, @@ -23,9 +23,14 @@ module.exports = ({ network, preload, peerId, keychain, repo, ipns, blockService print, pass }) - ipns.startOnline({ keychain, libp2p, peerId, repo }) - preload.start() + blockService.setExchange(bitswap) + + await Promise.all([ + ipns.startOnline({ keychain, libp2p, peerId, repo }), + preload.start(), + mfsPreload.start() + ]) } return start diff --git a/packages/ipfs-core/src/components/stop.js b/packages/ipfs-core/src/components/stop.js index d5ccc44c2c..5ff0412014 100644 --- a/packages/ipfs-core/src/components/stop.js +++ b/packages/ipfs-core/src/components/stop.js @@ -13,8 +13,8 @@ const Service = require('../utils/service') */ module.exports = ({ network, preload, blockService, ipns, repo, mfsPreload }) => { const stop = async () => { + blockService.unsetExchange() await Promise.all([ - blockService.unsetExchange(), preload.stop(), ipns.stop(), mfsPreload.stop(), diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js index 25c39b80e0..325a2f6f4b 100644 --- a/packages/ipfs-core/src/components/storage.js +++ b/packages/ipfs-core/src/components/storage.js @@ -2,7 +2,7 @@ const log = require('debug')('ipfs:components:peer:storage') const createRepo = require('../runtime/repo-nodejs') - +const getDefaultConfig = require('../runtime/config-nodejs') const { ERR_REPO_NOT_INITIALIZED } = require('ipfs-repo').errors const uint8ArrayFromString = require('uint8arrays/from-string') const uint8ArrayToString = require('uint8arrays/to-string') @@ -83,7 +83,7 @@ const loadRepo = async (repo, options) => { */ const openRepo = async (repo) => { // If repo is closed attempt to open it. - if (!repo.closed) { + if (repo.closed) { try { await repo.open() return null @@ -123,7 +123,7 @@ const initRepo = async (repo, options) => { // 3. Init new repo with provided `.config` and restored / initalized // peerd identity. const config = { - ...options.config, + ...mergeOptions(applyProfiles(getDefaultConfig(), options.profiles), options.config), Identity: identity } await repo.init(config) diff --git a/packages/ipfs-core/src/runtime/config-nodejs.js b/packages/ipfs-core/src/runtime/config-nodejs.js index a5f21fa0aa..774a3f21a8 100644 --- a/packages/ipfs-core/src/runtime/config-nodejs.js +++ b/packages/ipfs-core/src/runtime/config-nodejs.js @@ -1,5 +1,8 @@ 'use strict' +/** + * @returns {import('../components/config').IPFSConfig} + */ module.exports = () => ({ Addresses: { Swarm: [ diff --git a/packages/ipfs-core/test/create-node.spec.js b/packages/ipfs-core/test/create-node.spec.js index e666f040cd..c7c8abacc8 100644 --- a/packages/ipfs-core/test/create-node.spec.js +++ b/packages/ipfs-core/test/create-node.spec.js @@ -87,9 +87,8 @@ describe('create node', function () { expect(ipfs.isOnline()).to.be.false() }) - it('should create but not initialize and not start', async () => { + it('should create but not start', async () => { const ipfs = await IPFS.create({ - init: false, start: false, repo: tempRepo, config: { Addresses: { Swarm: [] } } diff --git a/packages/ipfs-core/test/init.spec.js b/packages/ipfs-core/test/init.spec.js index f43733adad..273594a7fb 100644 --- a/packages/ipfs-core/test/init.spec.js +++ b/packages/ipfs-core/test/init.spec.js @@ -21,20 +21,22 @@ describe('init', function () { let repo let cleanup - beforeEach(async () => { + const init = async (options) => { const res = await createNode({ - init: false, + init: options, start: false }) + ipfs = res.ipfs repo = res.repo cleanup = res.cleanup - }) + return ipfs + } afterEach(() => cleanup()) it('should init successfully', async () => { - await ipfs.init({ bits: 512 }) + await init({ bits: 512 }) const res = await repo.exists() expect(res).to.equal(true) @@ -46,7 +48,7 @@ describe('init', function () { }) it('should init successfully with a keychain pass', async () => { - await ipfs.init({ bits: 512, pass: nanoid() }) + await init({ bits: 512, pass: nanoid() }) const res = await repo.exists() expect(res).to.equal(true) @@ -60,7 +62,7 @@ describe('init', function () { }) it('should init with a key algorithm (ed25519)', async () => { - await ipfs.init({ algorithm: 'ed25519' }) + await init({ algorithm: 'ed25519' }) const config = await repo.config.getAll() const peerId = await PeerId.createFromPrivKey(config.Identity.PrivKey) @@ -68,7 +70,7 @@ describe('init', function () { }) it('should init with a key algorithm (secp256k1)', async () => { - await ipfs.init({ algorithm: 'secp256k1' }) + await init({ algorithm: 'secp256k1' }) const config = await repo.config.getAll() const peerId = await PeerId.createFromPrivKey(config.Identity.PrivKey) @@ -78,35 +80,35 @@ describe('init', function () { it('should set # of bits in key', async function () { this.timeout(120 * 1000) - await ipfs.init({ bits: 1024 }) + await init({ bits: 1024 }) const config = await repo.config.getAll() expect(config.Identity.PrivKey.length).is.above(256) }) it('should allow a pregenerated key to be used', async () => { - await ipfs.init({ privateKey }) + await init({ privateKey }) const config = await repo.config.getAll() expect(config.Identity.PeerID).is.equal('QmRsooYQasV5f5r834NSpdUtmejdQcpxXkK6qsozZWEihC') }) it('should allow a pregenerated ed25519 key to be used', async () => { - await ipfs.init({ privateKey: edPrivateKey }) + await init({ privateKey: edPrivateKey }) const config = await repo.config.getAll() expect(config.Identity.PeerID).is.equal('12D3KooWRm8J3iL796zPFi2EtGGtUJn58AG67gcqzMFHZnnsTzqD') }) it('should allow a pregenerated secp256k1 key to be used', async () => { - await ipfs.init({ privateKey: secpPrivateKey }) + await init({ privateKey: secpPrivateKey }) const config = await repo.config.getAll() expect(config.Identity.PeerID).is.equal('16Uiu2HAm5qw8UyXP2RLxQUx5KvtSN8DsTKz8quRGqGNC3SYiaB8E') }) it('should write init docs', async () => { - await ipfs.init({ bits: 512 }) + await init({ bits: 512 }) const multihash = 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB' const node = await ipfs.object.get(multihash, { enc: 'base58' }) @@ -114,7 +116,7 @@ describe('init', function () { }) it('should allow init with an empty repo', async () => { - await ipfs.init({ bits: 512, emptyRepo: true }) + await init({ bits: 512, emptyRepo: true }) // Should not have default assets const multihash = uint8ArrayFromString('12205e7c3ce237f936c76faf625e90f7751a9f5eeb048f59873303c215e9cce87599', 'base16') @@ -122,14 +124,14 @@ describe('init', function () { }) it('should apply one profile', async () => { - await ipfs.init({ bits: 512, profiles: ['test'] }) + await init({ bits: 512, profiles: ['test'] }) const config = await repo.config.getAll() expect(config.Bootstrap).to.be.empty() }) it('should apply multiple profiles', async () => { - await ipfs.init({ bits: 512, profiles: ['test', 'local-discovery'] }) + await init({ bits: 512, profiles: ['test', 'local-discovery'] }) const config = await repo.config.getAll() expect(config.Bootstrap).to.be.empty() From ac85e557173c233d23ca2dfbd7bdb94691190283 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 08:53:42 -0800 Subject: [PATCH 03/29] fix: rebase mistakes --- packages/ipfs-core/src/preload.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/ipfs-core/src/preload.js b/packages/ipfs-core/src/preload.js index 6168e6ff42..6c74a7d508 100644 --- a/packages/ipfs-core/src/preload.js +++ b/packages/ipfs-core/src/preload.js @@ -6,6 +6,9 @@ const CID = require('cids') const shuffle = require('array-shuffle') const AbortController = require('native-abort-controller') const preload = require('./runtime/preload-nodejs') +/** @type {typeof import('hashlru').default} */ +// @ts-ignore - hashlru has incorrect typedefs +const hashlru = require('hashlru') const log = Object.assign( debug('ipfs:preload'), @@ -18,6 +21,7 @@ const log = Object.assign( const createPreloader = (options = {}) => { options.enabled = Boolean(options.enabled) options.addresses = options.addresses || [] + options.cache = options.cache || 1000 if (!options.enabled || !options.addresses.length) { log('preload disabled') @@ -32,6 +36,9 @@ const createPreloader = (options = {}) => { let requests = [] const apiUris = options.addresses.map(toUri) + // Avoid preloading the same CID over and over again + const cache = hashlru(options.cache) + /** * @param {string|CID} path * @returns {Promise} @@ -44,6 +51,14 @@ const createPreloader = (options = {}) => { path = new CID(path).toString() } + if (cache.has(path)) { + // we've preloaded this recently, don't preload it again + return + } + + // make sure we don't preload this again any time soon + cache.set(path, true) + const fallbackApiUris = shuffle(apiUris) let success = false const now = Date.now() @@ -98,9 +113,9 @@ module.exports = createPreloader * @typedef {ReturnType} Preload * * @typedef {object} Options - * @property {boolean} [enabled] - Enable content preloading (Default: `true`) - * @property {number} [interval] - * @property {string[]} [addresses] - Multiaddr API addresses of nodes that should preload content. + * @property {boolean} [enabled = false] - Whether to preload anything + * @property {number} [cache = 1000] - How many CIDs to cache + * @property {string[]} [addresses = []] - Which preload servers to use. * **NOTE:** nodes specified here should also be added to your node's bootstrap address list at `config.Boostrap`. * * @typedef {import('./components').AbortOptions} AbortOptions From 90f18fc91da879395de0aba33a2f89287eef2a75 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 08:59:49 -0800 Subject: [PATCH 04/29] fix: documentation missmatch --- docs/core-api/BITSWAP.md | 4 ++-- packages/ipfs-core/src/components/bitswap/stat.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core-api/BITSWAP.md b/docs/core-api/BITSWAP.md index 786f19ace4..7caee34308 100644 --- a/docs/core-api/BITSWAP.md +++ b/docs/core-api/BITSWAP.md @@ -159,7 +159,7 @@ The returned object contains the following keys: - `provideBufLen` is an integer. - `wantlist` (array of [CID][cid]s) -- `peers` (array of peer IDs as Strings) +- `peers` (array of peer IDs reperesented by CIDs) - `blocksReceived` is a [BigNumber Int][1] - `dataReceived` is a [BigNumber Int][1] - `blocksSent` is a [BigNumber Int][1] @@ -194,4 +194,4 @@ A great source of [examples][] can be found in the tests for this API. [examples]: https://github.com/ipfs/js-ipfs/blob/master/packages/interface-ipfs-core/src/bitswap [cid]: https://www.npmjs.com/package/cids [peerid]: https://www.npmjs.com/package/peer-id -[AbortSignal]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal \ No newline at end of file +[AbortSignal]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal diff --git a/packages/ipfs-core/src/components/bitswap/stat.js b/packages/ipfs-core/src/components/bitswap/stat.js index 63ce7f5551..898af83771 100644 --- a/packages/ipfs-core/src/components/bitswap/stat.js +++ b/packages/ipfs-core/src/components/bitswap/stat.js @@ -59,7 +59,7 @@ module.exports = ({ network }) => { * @typedef {object} BitswapStats - An object that contains information about the bitswap agent * @property {number} provideBufLen - an integer * @property {CID[]} wantlist - * @property {CID[]} peers - array of peer IDs as Strings + * @property {CID[]} peers - array of peer IDs * @property {Big} blocksReceived * @property {Big} dataReceived * @property {Big} blocksSent From 015fcfc72d3904a1ed12c4f6640893685903391f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 09:01:32 -0800 Subject: [PATCH 05/29] chore: remove unnecessary change --- packages/ipfs-core/src/components/block/get.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ipfs-core/src/components/block/get.js b/packages/ipfs-core/src/components/block/get.js index 225ab16e1b..5b364a9c9e 100644 --- a/packages/ipfs-core/src/components/block/get.js +++ b/packages/ipfs-core/src/components/block/get.js @@ -8,7 +8,7 @@ const { withTimeoutOption } = require('../../utils') * @param {import('.').BlockService} config.blockService * @param {import('.').Preload} config.preload */ -module.exports = ({ preload, blockService }) => { +module.exports = ({ blockService, preload }) => { /** * Get a raw IPFS block. * From d321353a79c459766fb0d8b4a024d26e4533c78a Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 09:39:28 -0800 Subject: [PATCH 06/29] chore: undo accidental changes --- packages/ipfs-core/src/components/block/put.js | 2 +- packages/ipfs-core/src/components/block/rm.js | 2 +- packages/ipfs-core/src/components/dag/get.js | 1 - packages/ipfs-core/src/components/dag/put.js | 4 ++-- packages/ipfs-core/src/components/files/ls.js | 16 ++++++++++++---- packages/ipfs-core/src/components/id.js | 2 +- packages/ipfs-core/src/components/pin/add-all.js | 2 +- packages/ipfs-core/src/components/pin/rm-all.js | 4 ++-- packages/ipfs-core/src/components/refs/index.js | 2 +- packages/ipfs-core/src/mfs-preload.js | 6 +++++- packages/ipfs-core/src/runtime/config-nodejs.js | 6 ++---- 11 files changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/ipfs-core/src/components/block/put.js b/packages/ipfs-core/src/components/block/put.js index db395eb7ac..1d5d8010bf 100644 --- a/packages/ipfs-core/src/components/block/put.js +++ b/packages/ipfs-core/src/components/block/put.js @@ -13,7 +13,7 @@ const { withTimeoutOption } = require('../../utils') * @param {import('.').GCLock} config.gcLock * @param {import('.').Preload} config.preload */ -module.exports = ({ blockService, preload, gcLock, pin }) => { +module.exports = ({ blockService, pin, gcLock, preload }) => { /** * Stores input as an IPFS block. * diff --git a/packages/ipfs-core/src/components/block/rm.js b/packages/ipfs-core/src/components/block/rm.js index bbfe56f8f0..7df1118267 100644 --- a/packages/ipfs-core/src/components/block/rm.js +++ b/packages/ipfs-core/src/components/block/rm.js @@ -16,7 +16,7 @@ const BLOCK_RM_CONCURRENCY = 8 * @param {import('.').PinManager} config.pinManager * @param {import('.').GCLock} config.gcLock */ -module.exports = ({ gcLock, blockService, pinManager }) => { +module.exports = ({ blockService, gcLock, pinManager }) => { /** /** * Remove one or more IPFS block(s). diff --git a/packages/ipfs-core/src/components/dag/get.js b/packages/ipfs-core/src/components/dag/get.js index 5aecec6188..be4777e0a7 100644 --- a/packages/ipfs-core/src/components/dag/get.js +++ b/packages/ipfs-core/src/components/dag/get.js @@ -56,7 +56,6 @@ module.exports = ({ ipld, preload }) => { * ``` * * @param {CID|string} ipfsPath - A DAG node that follows one of the supported IPLD formats - * @param ipfsPath * @param {GetOptions & AbortOptions} [options] - An optional configration * @returns {Promise} */ diff --git a/packages/ipfs-core/src/components/dag/put.js b/packages/ipfs-core/src/components/dag/put.js index e15fbea195..563f63018f 100644 --- a/packages/ipfs-core/src/components/dag/put.js +++ b/packages/ipfs-core/src/components/dag/put.js @@ -11,12 +11,12 @@ const { withTimeoutOption } = require('../../utils') /** * @param {Object} config - * @param {import('.').Pin} config.pin * @param {import('.').IPLD} config.ipld + * @param {import('.').Pin} config.pin * @param {import('.').Preload} config.preload * @param {import('.').GCLock} config.gcLock */ -module.exports = ({ ipld, preload, pin, gcLock }) => { +module.exports = ({ ipld, pin, gcLock, preload }) => { /** * Store an IPLD format node * diff --git a/packages/ipfs-core/src/components/files/ls.js b/packages/ipfs-core/src/components/files/ls.js index 195002025f..4610c34763 100644 --- a/packages/ipfs-core/src/components/files/ls.js +++ b/packages/ipfs-core/src/components/files/ls.js @@ -3,7 +3,6 @@ const exporter = require('ipfs-unixfs-exporter') const toMfsPath = require('./utils/to-mfs-path') const { - MFS_FILE_TYPES, withTimeoutOption } = require('../../utils') @@ -12,14 +11,20 @@ const { * @returns {UnixFSEntry} */ const toOutput = (fsEntry) => { - let type = 0 + /** @type FileType */ + let type = 'file' let size = fsEntry.node.size || fsEntry.node.length let mode let mtime if (fsEntry.unixfs) { size = fsEntry.unixfs.fileSize() - type = MFS_FILE_TYPES[fsEntry.unixfs.type] + type = fsEntry.unixfs.type + + if (fsEntry.unixfs.type === 'hamt-sharded-directory') { + type = 'directory' + } + mode = fsEntry.unixfs.mode mtime = fsEntry.unixfs.mtime } @@ -89,10 +94,13 @@ module.exports = (context) => { * @property {number} [nsecs] - the number of nanoseconds since the last full * second. * + * @typedef {'file'|'directory'} FileType + * * @typedef {object} UnixFSEntry * @property {CID} cid + * @property {string} name * @property {number} [mode] * @property {UnixTimeObj} [mtime] * @property {number} size - * @property {number} type + * @property {FileType} type */ diff --git a/packages/ipfs-core/src/components/id.js b/packages/ipfs-core/src/components/id.js index 62afd8d6e5..6b5e23b7da 100644 --- a/packages/ipfs-core/src/components/id.js +++ b/packages/ipfs-core/src/components/id.js @@ -10,7 +10,7 @@ const uint8ArrayToString = require('uint8arrays/to-string') * @param {import('.').PeerId} config.peerId * @param {import('.').NetworkService} config.network */ -module.exports = ({ network, peerId }) => { +module.exports = ({ peerId, network }) => { /** * Returns the identity of the Peer * diff --git a/packages/ipfs-core/src/components/pin/add-all.js b/packages/ipfs-core/src/components/pin/add-all.js index 2c23f4db1f..001b685657 100644 --- a/packages/ipfs-core/src/components/pin/add-all.js +++ b/packages/ipfs-core/src/components/pin/add-all.js @@ -12,7 +12,7 @@ const normaliseInput = require('ipfs-core-utils/src/pins/normalise-input') * @param {import('.').DagReader} config.dagReader * @param {import('.').PinManager} config.pinManager */ -module.exports = ({ gcLock, dagReader, pinManager }) => { +module.exports = ({ pinManager, gcLock, dagReader }) => { /** * Adds multiple IPFS objects to the pinset and also stores it to the IPFS * repo. pinset is the set of hashes currently pinned (not gc'able) diff --git a/packages/ipfs-core/src/components/pin/rm-all.js b/packages/ipfs-core/src/components/pin/rm-all.js index a7f7e117ad..34b432a4c6 100644 --- a/packages/ipfs-core/src/components/pin/rm-all.js +++ b/packages/ipfs-core/src/components/pin/rm-all.js @@ -15,7 +15,7 @@ module.exports = ({ pinManager, gcLock, dagReader }) => { * Unpin one or more blocks from your repo * * @param {Source} source - Unpin all pins from the source - * @param {AbortOptions} [options] + * @param {AbortOptions} [_options] * @returns {AsyncIterable} * @example * ```js @@ -29,7 +29,7 @@ module.exports = ({ pinManager, gcLock, dagReader }) => { * // CID('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u') * ``` */ - async function * rmAll (source, options = {}) { + async function * rmAll (source, _options = {}) { const release = await gcLock.readLock() try { diff --git a/packages/ipfs-core/src/components/refs/index.js b/packages/ipfs-core/src/components/refs/index.js index 17557f67af..8258f7543e 100644 --- a/packages/ipfs-core/src/components/refs/index.js +++ b/packages/ipfs-core/src/components/refs/index.js @@ -42,9 +42,9 @@ module.exports = function ({ ipld, resolve, preload }) { options.maxDepth = options.recursive ? Infinity : 1 } + /** @type {(string|CID)[]} */ const rawPaths = Array.isArray(ipfsPath) ? ipfsPath : [ipfsPath] - // @ts-ignore const paths = rawPaths.map(p => getFullPath(preload, p, options)) for (const path of paths) { diff --git a/packages/ipfs-core/src/mfs-preload.js b/packages/ipfs-core/src/mfs-preload.js index 1716e1206c..e800eff9f4 100644 --- a/packages/ipfs-core/src/mfs-preload.js +++ b/packages/ipfs-core/src/mfs-preload.js @@ -10,7 +10,7 @@ const log = Object.assign(debug('ipfs:mfs-preload'), { * @param {Object} config * @param {import('./components').Preload} config.preload * @param {import('./components').Files} config.files - * @param {import('./components').PreloadOptions} [config.options] + * @param {Options} [config.options] */ module.exports = ({ preload, files, options = {} }) => { options.interval = options.interval || 30 * 1000 @@ -61,4 +61,8 @@ module.exports = ({ preload, files, options = {} }) => { /** * @typedef {ReturnType} MFSPreload + * @typedef {PreloadOptions & MFSPreloadOptions} Options + * @typedef {Object} MFSPreloadOptions + * @property {number} [interval] + * @typedef {import('./components').PreloadOptions} PreloadOptions */ diff --git a/packages/ipfs-core/src/runtime/config-nodejs.js b/packages/ipfs-core/src/runtime/config-nodejs.js index 774a3f21a8..dadd0b5c85 100644 --- a/packages/ipfs-core/src/runtime/config-nodejs.js +++ b/packages/ipfs-core/src/runtime/config-nodejs.js @@ -1,8 +1,5 @@ 'use strict' -/** - * @returns {import('../components/config').IPFSConfig} - */ module.exports = () => ({ Addresses: { Swarm: [ @@ -51,7 +48,8 @@ module.exports = () => ({ '/dns4/node3.preload.ipfs.io/tcp/443/wss/p2p/QmY7JB6MQXhxHvq7dBDh4HpbH29v4yE9JRadAVpndvzySN' ], Pubsub: { - Router: 'gossipsub', + /** @type {'gossipsub'} */ + Router: ('gossipsub'), Enabled: true }, Swarm: { From e42b9d78b7394f871e772cce57e5ece5d3bf0d83 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 10:27:54 -0800 Subject: [PATCH 07/29] fix: lint issues --- packages/ipfs-core/src/components/pin/pin-manager.js | 2 +- packages/ipfs-core/src/components/repo/version.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ipfs-core/src/components/pin/pin-manager.js b/packages/ipfs-core/src/components/pin/pin-manager.js index 8155d35460..53f16f119f 100644 --- a/packages/ipfs-core/src/components/pin/pin-manager.js +++ b/packages/ipfs-core/src/components/pin/pin-manager.js @@ -150,7 +150,7 @@ class PinManager { /** * @param {AbortOptions} [options] - * @returns AsyncIterable<{ cid: CID, metadata: any }> + * @returns {AsyncIterable<{ cid: CID, metadata: any }>} */ async * directKeys (options) { for await (const entry of this.repo.pins.query({ diff --git a/packages/ipfs-core/src/components/repo/version.js b/packages/ipfs-core/src/components/repo/version.js index e31390490a..aee51ccf0e 100644 --- a/packages/ipfs-core/src/components/repo/version.js +++ b/packages/ipfs-core/src/components/repo/version.js @@ -12,7 +12,7 @@ module.exports = ({ repo }) => { * If the repo has been initialized, report the current version. * Otherwise report the version that would be initialized. * - * @param options + * @param {import('.').AbortOptions} options * @returns {Promise} */ async function version (options) { From 877c620aacacf0adf9ed480a81d39c2582ea47f4 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 14:57:23 -0800 Subject: [PATCH 08/29] fix: ipns API regression --- packages/ipfs-core/src/components/index.js | 2 +- packages/ipfs-core/src/components/ipns.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 3fee9bffb1..eadb591abd 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -70,7 +70,7 @@ class IPFS { }) const dns = createDNSAPI() const isOnline = createIsOnlineAPI({ network }) - const ipns = new IPNSAPI() + const ipns = new IPNSAPI(options) const dagReader = DagAPI.reader({ ipld, preload }) const name = new NameAPI({ diff --git a/packages/ipfs-core/src/components/ipns.js b/packages/ipfs-core/src/components/ipns.js index eb9d255912..ca305ea3a9 100644 --- a/packages/ipfs-core/src/components/ipns.js +++ b/packages/ipfs-core/src/components/ipns.js @@ -8,7 +8,7 @@ const log = require('debug')('ipfs:components:ipns') class IPNSAPI { /** - * @param {Object} [options] + * @param {Object} options * @param {string} [options.pass] * @param {boolean} [options.offline] * @param {LibP2POptions} [options.libp2p] @@ -69,8 +69,8 @@ class IPNSAPI { if (this.online != null) { throw new AlreadyInitializedError() } - const routing = routingConfig({ libp2p, repo, peerId, options: this.options }) + const ipns = new IPNS(routing, repo.datastore, peerId, keychain, this.options) await ipns.republisher.start() this.online = ipns From f8dc797524f58f2236597d85071c7c8e9b749ae6 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 14:57:56 -0800 Subject: [PATCH 09/29] fix: dag put regression --- packages/ipfs-core/src/components/dag/put.js | 27 +++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/ipfs-core/src/components/dag/put.js b/packages/ipfs-core/src/components/dag/put.js index 563f63018f..adb50dc293 100644 --- a/packages/ipfs-core/src/components/dag/put.js +++ b/packages/ipfs-core/src/components/dag/put.js @@ -76,23 +76,38 @@ const readEncodingOptions = (options) => { throw new Error('Can\'t put dag node. Please provide `format` AND `hashAlg` options.') } - const cidVersion = readVersion(options) - const { hashAlg, format } = options.cid != null ? { format: options.cid.code, hashAlg: undefined } - : { ...defaultCIDOptions, ...options } + : encodingCodes({ ...defaultCIDOptions, ...options }) + const cidVersion = readVersion({ ...options, format, hashAlg }) return { cidVersion, - format: typeof format === 'string' ? nameToCodec(format) : format, - hashAlg: typeof hashAlg === 'string' ? nameToCodec(hashAlg) : hashAlg + format, + hashAlg } } +/** + * + * @param {Object} options + * @param {number|string} options.format + * @param {number|string} [options.hashAlg] + */ +const encodingCodes = ({ format, hashAlg }) => ({ + format: typeof format === 'string' ? nameToCodec(format) : format, + hashAlg: typeof hashAlg === 'string' ? nameToCodec(hashAlg) : hashAlg +}) + /** * Figures out what version of CID should be used given the options. * - * @param {PutOptions} options + * @param {Object} options + * @param {0|1} [options.version] + * @param {CID} [options.cid] + * @param {number} [options.format] + * @param {number} [options.hashAlg] + * @param options.version */ const readVersion = ({ version, cid, format, hashAlg }) => { // If version is passed just use that. From e757d582d251f6664b8c9dedd5267ddc95eeb57f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 15:18:22 -0800 Subject: [PATCH 10/29] chore: use patched ipfsd-ctl --- examples/browser-ipns-publish/package.json | 2 +- examples/explore-ethereum-blockchain/package.json | 2 +- examples/http-client-browser-pubsub/package.json | 2 +- examples/http-client-bundle-webpack/package.json | 2 +- examples/http-client-name-api/package.json | 2 +- packages/interface-ipfs-core/package.json | 2 +- packages/ipfs-core/package.json | 2 +- packages/ipfs-http-client/package.json | 2 +- packages/ipfs/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json index d5b84c0feb..d4bca8af4c 100644 --- a/examples/browser-ipns-publish/package.json +++ b/examples/browser-ipns-publish/package.json @@ -27,7 +27,7 @@ "devDependencies": { "delay": "^4.4.0", "execa": "^4.0.3", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "go-ipfs": "^0.7.0", "parcel-bundler": "^1.12.4", "path": "^0.12.7", diff --git a/examples/explore-ethereum-blockchain/package.json b/examples/explore-ethereum-blockchain/package.json index adcff2824a..86cb25cbc9 100644 --- a/examples/explore-ethereum-blockchain/package.json +++ b/examples/explore-ethereum-blockchain/package.json @@ -12,7 +12,7 @@ "devDependencies": { "ipfs": "^0.51.0", "ipfs-http-client": "^48.0.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "ipld-ethereum": "^5.0.1", "test-ipfs-example": "^2.0.3" } diff --git a/examples/http-client-browser-pubsub/package.json b/examples/http-client-browser-pubsub/package.json index a0ea4a5e76..079dc672ba 100644 --- a/examples/http-client-browser-pubsub/package.json +++ b/examples/http-client-browser-pubsub/package.json @@ -21,7 +21,7 @@ "execa": "^4.0.3", "go-ipfs": "^0.7.0", "ipfs": "^0.51.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "parcel-bundler": "^1.12.4", "test-ipfs-example": "^2.0.3" } diff --git a/examples/http-client-bundle-webpack/package.json b/examples/http-client-bundle-webpack/package.json index fd3aa06e51..62a0d5321d 100644 --- a/examples/http-client-bundle-webpack/package.json +++ b/examples/http-client-bundle-webpack/package.json @@ -25,7 +25,7 @@ "copy-webpack-plugin": "^5.0.4", "execa": "^4.0.3", "ipfs": "^0.51.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "react-hot-loader": "^4.12.21", "test-ipfs-example": "^2.0.3", "webpack": "^4.43.0", diff --git a/examples/http-client-name-api/package.json b/examples/http-client-name-api/package.json index 7756c64673..60f9433572 100644 --- a/examples/http-client-name-api/package.json +++ b/examples/http-client-name-api/package.json @@ -18,7 +18,7 @@ "devDependencies": { "execa": "^4.0.3", "go-ipfs": "^0.7.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "parcel-bundler": "^1.12.4", "test-ipfs-example": "^2.0.3" }, diff --git a/packages/interface-ipfs-core/package.json b/packages/interface-ipfs-core/package.json index 4b46bb4395..58aa717ded 100644 --- a/packages/interface-ipfs-core/package.json +++ b/packages/interface-ipfs-core/package.json @@ -67,7 +67,7 @@ }, "devDependencies": { "aegir": "^28.0.0", - "ipfsd-ctl": "^7.0.2" + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init" }, "contributors": [ "Alan Shaw ", diff --git a/packages/ipfs-core/package.json b/packages/ipfs-core/package.json index b35233c8ea..23d2be4f35 100644 --- a/packages/ipfs-core/package.json +++ b/packages/ipfs-core/package.json @@ -123,7 +123,7 @@ "aegir": "^28.0.0", "delay": "^4.4.0", "interface-ipfs-core": "^0.141.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "ipld-git": "^0.6.1", "iso-random-stream": "^1.1.1", "iso-url": "^0.4.7", diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index 6daf57d365..ba0e6de218 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -84,7 +84,7 @@ "go-ipfs": "^0.7.0", "interface-ipfs-core": "^0.141.0", "ipfs-core": "^0.1.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "it-all": "^1.0.4", "it-concat": "^1.0.1", "it-pipe": "^1.1.0", diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index 57d7c2a996..5e76c07b00 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -55,7 +55,7 @@ "ipfs-http-client": "^48.0.0", "ipfs-interop": "^3.0.0", "ipfs-utils": "^4.0.0", - "ipfsd-ctl": "^7.0.2", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", "iso-random-stream": "^1.1.1", "iso-url": "^0.4.7", "it-to-buffer": "^1.0.2", From 417abf7cd47f00c943daa209cc0ea50e024b5449 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 15:27:50 -0800 Subject: [PATCH 11/29] fix: remove duplicate param annoation --- packages/ipfs-core/src/components/dag/put.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ipfs-core/src/components/dag/put.js b/packages/ipfs-core/src/components/dag/put.js index adb50dc293..0aaf37e524 100644 --- a/packages/ipfs-core/src/components/dag/put.js +++ b/packages/ipfs-core/src/components/dag/put.js @@ -107,7 +107,6 @@ const encodingCodes = ({ format, hashAlg }) => ({ * @param {CID} [options.cid] * @param {number} [options.format] * @param {number} [options.hashAlg] - * @param options.version */ const readVersion = ({ version, cid, format, hashAlg }) => { // If version is passed just use that. From 8e63ab46e35259a09bc6ad028f467bc824044db6 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 1 Nov 2020 20:33:02 -0800 Subject: [PATCH 12/29] fix: init.bits should be a number --- packages/interface-ipfs-core/src/add.js | 2 +- packages/ipfs-cli/src/commands/init.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/interface-ipfs-core/src/add.js b/packages/interface-ipfs-core/src/add.js index fdf27d281e..d687ac88ab 100644 --- a/packages/interface-ipfs-core/src/add.js +++ b/packages/interface-ipfs-core/src/add.js @@ -55,7 +55,7 @@ module.exports = (common, options) => { after(() => common.clean()) - it('should respect timeout option when adding files', () => { + it('should respect timeout option when adding file', () => { return testTimeout(() => ipfs.add('Hello', { timeout: 1 })) diff --git a/packages/ipfs-cli/src/commands/init.js b/packages/ipfs-cli/src/commands/init.js index a324d87c9f..ff29400fde 100644 --- a/packages/ipfs-cli/src/commands/init.js +++ b/packages/ipfs-cli/src/commands/init.js @@ -27,7 +27,8 @@ module.exports = { type: 'number', alias: 'b', default: '2048', - describe: 'Number of bits to use in the generated RSA private key (defaults to 2048)' + describe: 'Number of bits to use in the generated RSA private key (defaults to 2048)', + coerce: Number }) .option('empty-repo', { alias: 'e', From 7816a7ef7b34457c823fc9ad66c87291a747839a Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 2 Nov 2020 10:31:42 -0800 Subject: [PATCH 13/29] fix: issues with ipld wiring --- packages/ipfs-core/src/components/index.js | 6 ++-- packages/ipfs-core/src/components/libp2p.js | 31 ++++++++++++----- packages/ipfs-core/src/components/network.js | 19 +++-------- packages/ipfs-core/src/components/start.js | 6 ++-- packages/ipfs-core/src/components/storage.js | 36 ++++++++++++-------- 5 files changed, 55 insertions(+), 43 deletions(-) diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index eadb591abd..9781404f83 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -55,7 +55,7 @@ class IPFS { * @param {Options} config.options */ constructor ({ print, storage, options }) { - const { peerId, repo, keychain, pass } = storage + const { peerId, repo, keychain } = storage const network = Service.create(Network) const preload = createPreloadAPI(options.preload) @@ -133,7 +133,7 @@ class IPFS { mfsPreload, print, keychain, - pass + options }) this.stop = createStopAPI({ @@ -212,7 +212,7 @@ class IPFS { await ipfs.preload.start() ipfs.ipns.startOffline(storage) - if (!storage.isInitialized && !init.emptyRepo) { + if (!storage.isNew && !init.emptyRepo) { // add empty unixfs dir object (go-ipfs assumes this exists) const cid = await addEmptyDir(ipfs) diff --git a/packages/ipfs-core/src/components/libp2p.js b/packages/ipfs-core/src/components/libp2p.js index a181bc3924..9d4500c4d5 100644 --- a/packages/ipfs-core/src/components/libp2p.js +++ b/packages/ipfs-core/src/components/libp2p.js @@ -8,20 +8,20 @@ const PubsubRouters = require('../runtime/libp2p-pubsub-routers-nodejs') /** * @param {Object} config * @param {Repo} config.repo - * @param {Options} [config.options] - * @param {PeerId} [config.peerId] - * @param {string[]|Multiaddr[]} [config.multiaddrs] - * @param {{pass?:string}} [config.keychainConfig] - * @param {Config} [config.config] + * @param {IPFSOptions} config.options + * @param {PeerId} config.peerId + * @param {Multiaddr[]} config.multiaddrs + * @param {KeychainConfig} [config.keychainConfig] + * @param {IPFSConfig} config.config * @returns {LibP2P} */ module.exports = ({ - options = {}, + options, peerId, multiaddrs = [], repo, keychainConfig = {}, - config = {} + config }) => { const { datastore, keys } = repo @@ -45,6 +45,17 @@ module.exports = ({ return new Libp2p(libp2pOptions) } +/** + * @param {Object} input + * @param {IPFSOptions} input.options + * @param {IPFSConfig} input.config + * @param {Repo['datastore']} input.datastore + * @param {Repo['keys']} input.keys + * @param {KeychainConfig} input.keychainConfig + * @param {PeerId} input.peerId + * @param {Multiaddr[]} input.multiaddrs + * @returns {Options} + */ function getLibp2pOptions ({ options, config, datastore, keys, keychainConfig, peerId, multiaddrs }) { const getPubsubRouter = () => { const router = get(config, 'Pubsub.Router') || 'gossipsub' @@ -143,10 +154,14 @@ function getLibp2pOptions ({ options, config, datastore, keys, keychainConfig, p } /** + * @typedef {Object} KeychainConfig + * @property {string} [pass] + * * @typedef {import('.').Repo} Repo * @typedef {import('.').Multiaddr} Multiaddr * @typedef {import('.').PeerId} PeerId + * @typedef {import('.').Options} IPFSOptions * @typedef {import('libp2p')} LibP2P * @typedef {import('libp2p').Options} Options - * @typedef {import('libp2p').Config} Config + * @typedef {import('.').IPFSConfig} IPFSConfig */ diff --git a/packages/ipfs-core/src/components/network.js b/packages/ipfs-core/src/components/network.js index d8b7575aac..e409dbe630 100644 --- a/packages/ipfs-core/src/components/network.js +++ b/packages/ipfs-core/src/components/network.js @@ -20,7 +20,7 @@ class Network { /** * @param {Options} options */ - static async start ({ peerId, repo, print, pass }) { + static async start ({ peerId, repo, print, options }) { // Need to ensure that repo is open as it could have been closed between // `init` and `start`. if (repo.closed) { @@ -30,25 +30,15 @@ class Network { const config = await repo.config.getAll() const libp2p = createLibP2P({ + options, repo, peerId, multiaddrs: readAddrs(peerId, config), - config, - keychainConfig: { - pass, - ...config.Keychain - } + config }) if (libp2p.keychain) { await libp2p.loadKeychain() - - // If it is a new repo we don't have Keychain persisted yet. - if (libp2p.keychain.opts && !config.Keychain) { - await repo.config.set('Keychain', { - dek: libp2p.keychain.opts.dek - }) - } } await libp2p.start() @@ -117,9 +107,10 @@ const WEBSOCKET_STAR_PROTO_CODE = 479 * @property {PeerId} options.peerId * @property {Repo} options.repo * @property {Print} options.print - * @property {string} [options.pass] + * @property {IPFSOptions} options.options * * @typedef {import('.').IPFSConfig} IPFSConfig + * @typedef {import('.').Options} IPFSOptions * @typedef {import('.').Repo} Repo * @typedef {import('.').Print} Print * @typedef {import('.').LibP2P} LibP2P diff --git a/packages/ipfs-core/src/components/start.js b/packages/ipfs-core/src/components/start.js index 32d0862a5c..a9d512f377 100644 --- a/packages/ipfs-core/src/components/start.js +++ b/packages/ipfs-core/src/components/start.js @@ -13,15 +13,15 @@ const Service = require('../utils/service') * @param {import('.').MFSPreload} config.mfsPreload * @param {import('.').IPNS} config.ipns * @param {import('.').Keychain} config.keychain - * @param {string} [config.pass] + * @param {import('.').Options} config.options */ -module.exports = ({ network, preload, peerId, keychain, repo, ipns, blockService, mfsPreload, print, pass }) => { +module.exports = ({ network, preload, peerId, keychain, repo, ipns, blockService, mfsPreload, print, options }) => { const start = async () => { const { bitswap, libp2p } = await Service.start(network, { peerId, repo, print, - pass + options }) blockService.setExchange(bitswap) diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js index 325a2f6f4b..9016d77a37 100644 --- a/packages/ipfs-core/src/components/storage.js +++ b/packages/ipfs-core/src/components/storage.js @@ -19,17 +19,15 @@ class Storage { * @param {Keychain} keychain * @param {Repo} repo * @param {Print} print - * @param {string|undefined} pass - * @param {boolean} isInitialized + * @param {boolean} isNew */ - constructor (peerId, keychain, repo, print, pass, isInitialized) { + constructor (peerId, keychain, repo, print, isNew) { this.print = print this.peerId = peerId this.keychain = keychain this.repo = repo this.print = print - this.pass = pass - this.isInitialized = isInitialized + this.isNew = isNew } /** @@ -37,15 +35,15 @@ class Storage { * @param {Options} options */ static async start (options) { - const { repoAutoMigrate: autoMigrate, repo: repoInit, print, pass } = options + const { repoAutoMigrate: autoMigrate, repo: inputRepo, print, silent } = options - const repo = (typeof repoInit === 'string' || repoInit == null) - ? createRepo({ path: repoInit, autoMigrate }) - : repoInit + const repo = (typeof inputRepo === 'string' || inputRepo == null) + ? createRepo({ path: inputRepo, autoMigrate, silent }) + : inputRepo - const { peerId, keychain, isInitialized } = await loadRepo(repo, options) + const { peerId, keychain, isNew } = await loadRepo(repo, options) - return new Storage(peerId, keychain, repo, print, pass, isInitialized) + return new Storage(peerId, keychain, repo, print, isNew) } } module.exports = Storage @@ -54,20 +52,20 @@ module.exports = Storage * * @param {Repo} repo * @param {RepoOptions & InitOptions} options - * @returns {Promise<{peerId: PeerId, keychain:Keychain, isInitialized:boolean }>} + * @returns {Promise<{peerId: PeerId, keychain:Keychain, isNew:boolean }>} */ const loadRepo = async (repo, options) => { const openError = await openRepo(repo) if (openError == null) { // If opened succefully configure repo - return { ...await configureRepo(repo, options), isInitialized: true } + return { ...await configureRepo(repo, options), isNew: true } } else if (openError.code === ERR_REPO_NOT_INITIALIZED) { if (options.allowNew === false) { throw new NotEnabledError('new repo initialization is not enabled') } else { // If failed to open, because repo isn't initilaized and initalizing a // new repo allowed, init repo: - return { ...await initRepo(repo, options), isInitialized: false } + return { ...await initRepo(repo, options), isNew: false } } } else { throw openError @@ -135,6 +133,8 @@ const initRepo = async (repo, options) => { // Create libp2p for Keychain creation const libp2p = createLibP2P({ + options: {}, + multiaddrs: [], peerId, repo, config, @@ -210,6 +210,8 @@ const configureRepo = async (repo, { config, profiles, pass }) => { // @ts-ignore - Identity may not be present const peerId = await PeerId.createFromPrivKey(changed.Identity.PrivKey) const libp2p = createLibP2P({ + options: {}, + multiaddrs: [], peerId, repo, config: changed, @@ -218,7 +220,10 @@ const configureRepo = async (repo, { config, profiles, pass }) => { ...changed.Keychain } }) - libp2p.keychain && await libp2p.loadKeychain() + + if (libp2p.keychain) { + await libp2p.loadKeychain() + } return { peerId, keychain: libp2p.keychain } } @@ -264,6 +269,7 @@ const applyProfiles = (config, profiles) => { * @typedef {Object} RepoOptions * @property {Print} print * @property {IPFSConfig} [config] + * @property {boolean} [silent] * * @typedef {Object} ConfigureOptions * @property {IPFSConfig} [options.config] From 8b386cb58ebb7e259a9989ead203d6541d94d9c0 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 2 Nov 2020 10:57:26 -0800 Subject: [PATCH 14/29] fix: handle optionals differently --- packages/ipfs-core/src/components/libp2p.js | 14 +++++++------- packages/ipfs-core/src/components/network.js | 3 ++- packages/ipfs-core/src/components/storage.js | 8 ++++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/ipfs-core/src/components/libp2p.js b/packages/ipfs-core/src/components/libp2p.js index 9d4500c4d5..cd1868b081 100644 --- a/packages/ipfs-core/src/components/libp2p.js +++ b/packages/ipfs-core/src/components/libp2p.js @@ -8,20 +8,20 @@ const PubsubRouters = require('../runtime/libp2p-pubsub-routers-nodejs') /** * @param {Object} config * @param {Repo} config.repo - * @param {IPFSOptions} config.options + * @param {IPFSOptions|undefined} config.options * @param {PeerId} config.peerId - * @param {Multiaddr[]} config.multiaddrs - * @param {KeychainConfig} [config.keychainConfig] - * @param {IPFSConfig} config.config + * @param {Multiaddr[]|undefined} config.multiaddrs + * @param {KeychainConfig|undefined} config.keychainConfig + * @param {Partial|undefined} config.config * @returns {LibP2P} */ module.exports = ({ - options, + options = {}, peerId, multiaddrs = [], repo, keychainConfig = {}, - config + config = {} }) => { const { datastore, keys } = repo @@ -48,7 +48,7 @@ module.exports = ({ /** * @param {Object} input * @param {IPFSOptions} input.options - * @param {IPFSConfig} input.config + * @param {Partial} input.config * @param {Repo['datastore']} input.datastore * @param {Repo['keys']} input.keys * @param {KeychainConfig} input.keychainConfig diff --git a/packages/ipfs-core/src/components/network.js b/packages/ipfs-core/src/components/network.js index e409dbe630..670092ea6e 100644 --- a/packages/ipfs-core/src/components/network.js +++ b/packages/ipfs-core/src/components/network.js @@ -34,7 +34,8 @@ class Network { repo, peerId, multiaddrs: readAddrs(peerId, config), - config + config, + keychainConfig: undefined }) if (libp2p.keychain) { diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js index 9016d77a37..a079788d93 100644 --- a/packages/ipfs-core/src/components/storage.js +++ b/packages/ipfs-core/src/components/storage.js @@ -133,8 +133,8 @@ const initRepo = async (repo, options) => { // Create libp2p for Keychain creation const libp2p = createLibP2P({ - options: {}, - multiaddrs: [], + options: undefined, + multiaddrs: undefined, peerId, repo, config, @@ -210,8 +210,8 @@ const configureRepo = async (repo, { config, profiles, pass }) => { // @ts-ignore - Identity may not be present const peerId = await PeerId.createFromPrivKey(changed.Identity.PrivKey) const libp2p = createLibP2P({ - options: {}, - multiaddrs: [], + options: undefined, + multiaddrs: undefined, peerId, repo, config: changed, From caca4b9bcf386484ccdca63296d41d1207f25ff8 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 2 Nov 2020 13:15:27 -0800 Subject: [PATCH 15/29] chroe: inverse property name to reflect it meaning --- packages/ipfs-core/src/components/index.js | 2 +- packages/ipfs-core/src/components/storage.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 9781404f83..ff868aa108 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -212,7 +212,7 @@ class IPFS { await ipfs.preload.start() ipfs.ipns.startOffline(storage) - if (!storage.isNew && !init.emptyRepo) { + if (storage.isNew && !init.emptyRepo) { // add empty unixfs dir object (go-ipfs assumes this exists) const cid = await addEmptyDir(ipfs) diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js index a079788d93..6e210808e1 100644 --- a/packages/ipfs-core/src/components/storage.js +++ b/packages/ipfs-core/src/components/storage.js @@ -58,14 +58,14 @@ const loadRepo = async (repo, options) => { const openError = await openRepo(repo) if (openError == null) { // If opened succefully configure repo - return { ...await configureRepo(repo, options), isNew: true } + return { ...await configureRepo(repo, options), isNew: false } } else if (openError.code === ERR_REPO_NOT_INITIALIZED) { if (options.allowNew === false) { throw new NotEnabledError('new repo initialization is not enabled') } else { // If failed to open, because repo isn't initilaized and initalizing a // new repo allowed, init repo: - return { ...await initRepo(repo, options), isNew: false } + return { ...await initRepo(repo, options), isNew: true } } } else { throw openError From f5e0e8575a2e1b78b46e56e28ab5828d7e65511f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 2 Nov 2020 13:16:25 -0800 Subject: [PATCH 16/29] fix: incorrectly defined getter --- packages/ipfs-core/src/components/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index ff868aa108..5bf6b29e42 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -171,9 +171,11 @@ class IPFS { this.swarm = new SwarmAPI({ network }) // For the backwards compatibility - Object.defineProperty(this, 'libp2p', () => { - const net = network.try() - return net ? net.libp2p : undefined + Object.defineProperty(this, 'libp2p', { + get () { + const net = network.try() + return net ? net.libp2p : undefined + } }) } From 4fbd20bfedb44393ff5ddcab1efc03750c2f7539 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 2 Nov 2020 13:31:32 -0800 Subject: [PATCH 17/29] chroe: mark init deprecated --- packages/ipfs-core/src/components/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 5bf6b29e42..7aab6fa327 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -179,8 +179,13 @@ class IPFS { }) } + /** + * `IPFS.create` will do the initialization. Keep this around for backwards + * compatibility. + * + * @deprecated + */ async init () { // eslint-disable-line require-await - // Just keep this around for backwards compatibility throw new AlreadyInitializedError() } From fa7c2c13c43bab922052104fe8aff863d9725e9b Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 2 Nov 2020 14:49:57 -0800 Subject: [PATCH 18/29] fix: stop libp2p when node is stopped --- packages/ipfs-core/src/components/network.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/ipfs-core/src/components/network.js b/packages/ipfs-core/src/components/network.js index 670092ea6e..8da890a3d5 100644 --- a/packages/ipfs-core/src/components/network.js +++ b/packages/ipfs-core/src/components/network.js @@ -57,9 +57,11 @@ class Network { /** * @param {Network} network */ - // eslint-disable-next-line require-await static async stop (network) { - network.bitswap.stop() + await Promise.all([ + network.bitswap.stop(), + network.libp2p.stop() + ]) } } module.exports = Network From a9111549dab3402ab767effb6a35757ac199bf59 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 12 Nov 2020 18:54:00 -0800 Subject: [PATCH 19/29] Apply suggestions from code review Co-authored-by: Alex Potsides --- docs/core-api/BITSWAP.md | 2 +- packages/interface-ipfs-core/src/add.js | 2 +- packages/ipfs-core/src/components/config.js | 2 +- packages/ipfs-core/src/components/index.js | 2 +- packages/ipfs-core/src/components/storage.js | 2 +- packages/ipfs-core/src/utils/service.js | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/core-api/BITSWAP.md b/docs/core-api/BITSWAP.md index 7caee34308..f8f6d3c02a 100644 --- a/docs/core-api/BITSWAP.md +++ b/docs/core-api/BITSWAP.md @@ -159,7 +159,7 @@ The returned object contains the following keys: - `provideBufLen` is an integer. - `wantlist` (array of [CID][cid]s) -- `peers` (array of peer IDs reperesented by CIDs) +- `peers` (array of peer IDs represented by CIDs) - `blocksReceived` is a [BigNumber Int][1] - `dataReceived` is a [BigNumber Int][1] - `blocksSent` is a [BigNumber Int][1] diff --git a/packages/interface-ipfs-core/src/add.js b/packages/interface-ipfs-core/src/add.js index d687ac88ab..8330b9b50d 100644 --- a/packages/interface-ipfs-core/src/add.js +++ b/packages/interface-ipfs-core/src/add.js @@ -55,7 +55,7 @@ module.exports = (common, options) => { after(() => common.clean()) - it('should respect timeout option when adding file', () => { + it('should respect timeout option when adding a file', () => { return testTimeout(() => ipfs.add('Hello', { timeout: 1 })) diff --git a/packages/ipfs-core/src/components/config.js b/packages/ipfs-core/src/components/config.js index ab585a132e..38e1d304a3 100644 --- a/packages/ipfs-core/src/components/config.js +++ b/packages/ipfs-core/src/components/config.js @@ -24,7 +24,7 @@ module.exports = ({ repo }) => { * @param {AbortOptions} [options] */ async function getAll (options = {}) { - return await repo.config.getAll() + return await repo.config.getAll(options) } /** diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 7aab6fa327..1b07057003 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -286,7 +286,7 @@ const getDefaultOptions = () => ({ * * @typedef {Object} IPFSOptions * Options argument can be used to specify advanced configuration. - * @property {InitOptions|boolean} [init=true] - Perform repo initialization steps when creating + * @property {InitOptions} [init] - Initialization options * the IPFS node. * Note that *initializing* a repo is different from creating an instance of * [`ipfs.Repo`](https://github.com/ipfs/js-ipfs-repo). The IPFS constructor diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js index 6e210808e1..1d05d34506 100644 --- a/packages/ipfs-core/src/components/storage.js +++ b/packages/ipfs-core/src/components/storage.js @@ -57,7 +57,7 @@ module.exports = Storage const loadRepo = async (repo, options) => { const openError = await openRepo(repo) if (openError == null) { - // If opened succefully configure repo + // If opened successfully configure repo return { ...await configureRepo(repo, options), isNew: false } } else if (openError.code === ERR_REPO_NOT_INITIALIZED) { if (options.allowNew === false) { diff --git a/packages/ipfs-core/src/utils/service.js b/packages/ipfs-core/src/utils/service.js index a8eea7ab4e..fda5020b84 100644 --- a/packages/ipfs-core/src/utils/service.js +++ b/packages/ipfs-core/src/utils/service.js @@ -192,7 +192,7 @@ class Service { * Allows you to asynchronously obtain service implementation. If service * is starting it will await for completion. If service is stopped or stopping * this will (async) throw exception. This allows components that need to use - * this service convinient API to do it. + * this service convenient API to do it. * * @param {AbortOptions} [options] - Abort options. * @returns {Promise} @@ -223,8 +223,8 @@ module.exports = Service * @typedef {ReturnType extends ? Promise ? U : ReturnType} State */ /** - * Reperests service state which can be not started in which case - * it is instaceof `Error`. Pending in which case it's promise or + * Represents service state which can be not started in which case + * it is instanceof `Error`. Pending in which case it's promise or * ready in which case it is the value itself. * * @template T From b2425304139b67971edf4bf2745629b3a94aa8c9 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 12 Nov 2020 21:58:10 -0800 Subject: [PATCH 20/29] fix: identation --- packages/ipfs-core/src/components/dag/get.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/ipfs-core/src/components/dag/get.js b/packages/ipfs-core/src/components/dag/get.js index be4777e0a7..991b827406 100644 --- a/packages/ipfs-core/src/components/dag/get.js +++ b/packages/ipfs-core/src/components/dag/get.js @@ -18,12 +18,12 @@ module.exports = ({ ipld, preload }) => { * ```js * // example obj * const obj = { - * a: 1, - * b: [1, 2, 3], - * c: { - * ca: [5, 6, 7], - * cb: 'foo' - * } + * a: 1, + * b: [1, 2, 3], + * c: { + * ca: [5, 6, 7], + * cb: 'foo' + * } * } * * const cid = await ipfs.dag.put(obj, { format: 'dag-cbor', hashAlg: 'sha2-256' }) From 9064688f1ca202246f3e224d3c7d101b505fc7cc Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 12 Nov 2020 22:26:54 -0800 Subject: [PATCH 21/29] chore: rename core to root --- packages/ipfs-core/src/components/index.js | 5 ++--- packages/ipfs-core/src/components/ls.js | 4 ++-- packages/ipfs-core/src/components/{core.js => root.js} | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) rename packages/ipfs-core/src/components/{core.js => root.js} (96%) diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 1b07057003..27e408016e 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -23,7 +23,7 @@ const createRefsLocalAPI = require('./refs/local') const BitswapAPI = require('./bitswap') const BootstrapAPI = require('./bootstrap') const BlockAPI = require('./block') -const CoreAPI = require('./core') +const RootAPI = require('./root') const createVersionAPI = require('./version') const createIDAPI = require('./id') const createConfigAPI = require('./config') @@ -90,7 +90,7 @@ class IPFS { const refs = Object.assign(createRefsAPI({ ipld, resolve, preload }), { local: createRefsLocalAPI({ repo: storage.repo }) }) - const { add, addAll, cat, get, ls } = new CoreAPI({ + const { add, addAll, cat, get, ls } = new RootAPI({ gcLock, preload, pin, @@ -267,7 +267,6 @@ const addEmptyDir = async (ipfs) => { * @returns {Options} */ const getDefaultOptions = () => ({ - init: true, start: true, EXPERIMENTAL: {}, preload: { diff --git a/packages/ipfs-core/src/components/ls.js b/packages/ipfs-core/src/components/ls.js index 88b2866993..d65ac3b38c 100644 --- a/packages/ipfs-core/src/components/ls.js +++ b/packages/ipfs-core/src/components/ls.js @@ -6,8 +6,8 @@ const { normalizeCidPath, mapFile, withTimeoutOption } = require('../utils') /** * @param {Object} config - * @param {import('./core').IPLD} config.ipld - * @param {import('./core').Preload} config.preload + * @param {import('./root').IPLD} config.ipld + * @param {import('./root').Preload} config.preload */ module.exports = function ({ ipld, preload }) { /** diff --git a/packages/ipfs-core/src/components/core.js b/packages/ipfs-core/src/components/root.js similarity index 96% rename from packages/ipfs-core/src/components/core.js rename to packages/ipfs-core/src/components/root.js index 69734f94f6..bb89644d4f 100644 --- a/packages/ipfs-core/src/components/core.js +++ b/packages/ipfs-core/src/components/root.js @@ -6,7 +6,7 @@ const createCatAPI = require('./cat') const createGetAPI = require('./get') const createLsAPI = require('./ls') -class CoreAPI { +class RootAPI { /** * @param {Object} config * @param {Block} config.block @@ -33,7 +33,7 @@ class CoreAPI { } } -module.exports = CoreAPI +module.exports = RootAPI /** * @typedef {import('.').Block} Block From 4b6d668c395775876b17f6a35fd0de78625fad51 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 12 Nov 2020 23:08:17 -0800 Subject: [PATCH 22/29] Address review comments --- packages/ipfs-cli/src/daemon.js | 2 +- packages/ipfs-core/src/components/block/get.js | 4 ++-- packages/ipfs-core/src/components/config.js | 16 ++++++++-------- packages/ipfs-core/src/components/dag/get.js | 4 ++-- packages/ipfs-core/src/components/dag/tree.js | 2 +- packages/ipfs-core/src/components/index.js | 4 ---- packages/ipfs-core/src/components/key/export.js | 5 ++--- packages/ipfs-core/src/components/key/gen.js | 11 ++++------- packages/ipfs-core/src/components/key/import.js | 12 +++--------- packages/ipfs-core/src/components/key/info.js | 6 ++---- packages/ipfs-core/src/components/key/list.js | 4 +--- packages/ipfs-core/src/components/key/rm.js | 4 +--- packages/ipfs-core/src/utils.js | 6 ------ 13 files changed, 27 insertions(+), 53 deletions(-) diff --git a/packages/ipfs-cli/src/daemon.js b/packages/ipfs-cli/src/daemon.js index 9aef331312..725dc9c4f7 100644 --- a/packages/ipfs-cli/src/daemon.js +++ b/packages/ipfs-cli/src/daemon.js @@ -41,7 +41,7 @@ class Daemon { : this._options.repo // start the daemon - const ipfsOpts = Object.assign({}, { init: true, start: true, libp2p: getLibp2p }, this._options, { repo }) + const ipfsOpts = Object.assign({}, { start: true, libp2p: getLibp2p }, this._options, { repo }) const ipfs = this._ipfs = await IPFS.create(ipfsOpts) // start HTTP servers (if API or Gateway is enabled in options) diff --git a/packages/ipfs-core/src/components/block/get.js b/packages/ipfs-core/src/components/block/get.js index 5b364a9c9e..10ea440559 100644 --- a/packages/ipfs-core/src/components/block/get.js +++ b/packages/ipfs-core/src/components/block/get.js @@ -22,14 +22,14 @@ module.exports = ({ blockService, preload }) => { * console.log(block.data) * ``` */ - async function get (cid, options = {}) { + async function get (cid, options = {}) { // eslint-disable-line require-await cid = cleanCid(cid) if (options.preload !== false) { preload(cid) } - return await blockService.get(cid, options) + return blockService.get(cid, options) } return withTimeoutOption(get) diff --git a/packages/ipfs-core/src/components/config.js b/packages/ipfs-core/src/components/config.js index 38e1d304a3..20bb837baa 100644 --- a/packages/ipfs-core/src/components/config.js +++ b/packages/ipfs-core/src/components/config.js @@ -23,8 +23,8 @@ module.exports = ({ repo }) => { /** * @param {AbortOptions} [options] */ - async function getAll (options = {}) { - return await repo.config.getAll(options) + async function getAll (options = {}) { // eslint-disable-line require-await + return repo.config.getAll(options) } /** @@ -32,12 +32,12 @@ module.exports = ({ repo }) => { * @param {string} key * @param {AbortOptions} [options] */ - async function get (key, options) { + async function get (key, options) { // eslint-disable-line require-await if (!key) { return Promise.reject(new Error('key argument is required')) } - return await repo.config.get(key, options) + return repo.config.get(key, options) } /** @@ -46,16 +46,16 @@ module.exports = ({ repo }) => { * @param {ToJSON} value * @param {AbortOptions} [options] */ - async function set (key, value, options) { - return await repo.config.set(key, value, options) + async function set (key, value, options) { // eslint-disable-line require-await + return repo.config.set(key, value, options) } /** * @param {IPFSConfig} value * @param {AbortOptions} [options] */ - async function replace (value, options) { - return await repo.config.replace(value, options) + async function replace (value, options) { // eslint-disable-line require-await + return repo.config.replace(value, options) } /** diff --git a/packages/ipfs-core/src/components/dag/get.js b/packages/ipfs-core/src/components/dag/get.js index 991b827406..e5b9a6ba3a 100644 --- a/packages/ipfs-core/src/components/dag/get.js +++ b/packages/ipfs-core/src/components/dag/get.js @@ -31,8 +31,8 @@ module.exports = ({ ipld, preload }) => { * // zdpuAmtur968yprkhG9N5Zxn6MFVoqAWBbhUAkNLJs2UtkTq5 * * async function getAndLog(cid, path) { - * const result = await ipfs.dag.get(cid, { path }) - * console.log(result.value) + * const result = await ipfs.dag.get(cid, { path }) + * console.log(result.value) * } * * await getAndLog(cid, '/a') diff --git a/packages/ipfs-core/src/components/dag/tree.js b/packages/ipfs-core/src/components/dag/tree.js index 1c8fa3e41e..d416efe45a 100644 --- a/packages/ipfs-core/src/components/dag/tree.js +++ b/packages/ipfs-core/src/components/dag/tree.js @@ -47,7 +47,7 @@ module.exports = ({ ipld, preload }) => { * // c/cb * ``` */ - async function * tree (ipfsPath, options = {}) { + async function * tree (ipfsPath, options = {}) { // eslint-disable-line require-await const { cid, path diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 27e408016e..13171170fa 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -198,10 +198,6 @@ class IPFS { // eslint-disable-next-line no-console const print = options.silent ? log : console.log - if (options.init === false) { - throw new Error('Creating a non-initalized repo is no longer supported') - } - const init = { ...mergeOptions(initOptions(options), options), print diff --git a/packages/ipfs-core/src/components/key/export.js b/packages/ipfs-core/src/components/key/export.js index 056ba49c15..18c621d2b9 100644 --- a/packages/ipfs-core/src/components/key/export.js +++ b/packages/ipfs-core/src/components/key/export.js @@ -26,9 +26,8 @@ module.exports = ({ keychain }) => { * @param {import('.').AbortOptions} options * @returns {Promise} - The string representation of the key */ - const exportKey = async (name, password, options) => { - return await keychain.exportKey(name, password, options) - } + const exportKey = (name, password, options) => + keychain.exportKey(name, password, options) return withTimeoutOption(exportKey) } diff --git a/packages/ipfs-core/src/components/key/gen.js b/packages/ipfs-core/src/components/key/gen.js index e8d679d7f7..97d480e966 100644 --- a/packages/ipfs-core/src/components/key/gen.js +++ b/packages/ipfs-core/src/components/key/gen.js @@ -24,10 +24,10 @@ module.exports = ({ keychain }) => { * * @param {string} name - The name to give the key * @param {GenOptions & AbortOptions} options - * @returns {Promise<>} + * @returns {Promise} */ - const gen = async (name, options = {}) => { - return await keychain.createKey(name, options.type || 'rsa', options.size || 2048) + const gen = (name, options = {}) => { + return keychain.createKey(name, options.type || 'rsa', options.size || 2048) } return withTimeoutOption(gen) @@ -38,9 +38,6 @@ module.exports = ({ keychain }) => { * @property {import('libp2p-crypto').KeyType} [type='RSA'] - The key type * @property {number} [size=2048] - The key size in bits * - * @typedef {Object} Key - * @property {string} id - * @property {string} name - * + * @typedef {import('.').Key} Key * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/key/import.js b/packages/ipfs-core/src/components/key/import.js index 37b439d798..6477fdcd7d 100644 --- a/packages/ipfs-core/src/components/key/import.js +++ b/packages/ipfs-core/src/components/key/import.js @@ -22,17 +22,11 @@ module.exports = ({ keychain }) => { * @param {string} pem - The PEM encoded key * @param {string} password - The password that protects the PEM key * @param {import('.').AbortOptions} options - * @returns {Promise} - An object that describes the new key + * @returns {Promise} - An object that describes the new key */ - const importKey = async (name, pem, password, options) => { - return await keychain.importKey(name, pem, password, options) + const importKey = (name, pem, password, options) => { + return keychain.importKey(name, pem, password, options) } return withTimeoutOption(importKey) } - -/** - * @typedef {Object} ImportedKey - * @property {string} id - * @property {string} name - */ diff --git a/packages/ipfs-core/src/components/key/info.js b/packages/ipfs-core/src/components/key/info.js index 4f396e443c..a121604509 100644 --- a/packages/ipfs-core/src/components/key/info.js +++ b/packages/ipfs-core/src/components/key/info.js @@ -12,14 +12,12 @@ module.exports = ({ keychain }) => { * @param {AbortOptions} [options] * @returns {Promise} */ - const info = async (name, options = {}) => { - return await keychain.findKeyByName(name, options) - } + const info = (name, options = {}) => keychain.findKeyByName(name, options) return withTimeoutOption(info) } /** - * @typedef {import('./gen').Key} Key + * @typedef {import('.').Key} Key * @typedef {import('.').AbortOptions} AbortOptions */ diff --git a/packages/ipfs-core/src/components/key/list.js b/packages/ipfs-core/src/components/key/list.js index 2fcda87a30..db66268810 100644 --- a/packages/ipfs-core/src/components/key/list.js +++ b/packages/ipfs-core/src/components/key/list.js @@ -26,9 +26,7 @@ module.exports = ({ keychain }) => { * @param {AbortOptions} [options] * @returns {Promise} */ - const list = async (options = {}) => { - return await keychain.listKeys(options) - } + const list = (options = {}) => keychain.listKeys(options) return withTimeoutOption(list) } diff --git a/packages/ipfs-core/src/components/key/rm.js b/packages/ipfs-core/src/components/key/rm.js index 1b91de0466..c8b0355773 100644 --- a/packages/ipfs-core/src/components/key/rm.js +++ b/packages/ipfs-core/src/components/key/rm.js @@ -23,9 +23,7 @@ module.exports = ({ keychain }) => { * @param {import('.').AbortOptions} options * @returns {Promise} - An object that describes the removed key */ - const rm = async (name, options) => { - return await keychain.removeKey(name, options) - } + const rm = (name, options) => keychain.removeKey(name, options) return withTimeoutOption(rm) } diff --git a/packages/ipfs-core/src/utils.js b/packages/ipfs-core/src/utils.js index 3c81077388..258ff80401 100644 --- a/packages/ipfs-core/src/utils.js +++ b/packages/ipfs-core/src/utils.js @@ -18,12 +18,6 @@ exports.mergeOptions = mergeOptions const ERR_BAD_PATH = 'ERR_BAD_PATH' exports.OFFLINE_ERROR = 'This command must be run in online mode. Try running \'ipfs daemon\' first.' - -exports.MFS_FILE_TYPES = { - file: 0, - directory: 1, - 'hamt-sharded-directory': 1 -} exports.MFS_ROOT_KEY = new Key('/local/filesroot') exports.MFS_MAX_CHUNK_SIZE = 262144 exports.MFS_MAX_LINKS = 174 From eb66f38c62b534c13d9417c541ea918827b095eb Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 13 Nov 2020 11:50:47 -0800 Subject: [PATCH 23/29] Update packages/ipfs-core/src/components/dag/index.js Co-authored-by: Hugo Dias --- packages/ipfs-core/src/components/dag/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ipfs-core/src/components/dag/index.js b/packages/ipfs-core/src/components/dag/index.js index 31e0c3b634..ccb49b2986 100644 --- a/packages/ipfs-core/src/components/dag/index.js +++ b/packages/ipfs-core/src/components/dag/index.js @@ -57,8 +57,8 @@ module.exports = DagAPI * @property {ReturnType} tree * * @typedef {Object} ReaderConfig - * @property {IPLD} config.ipld - * @property {Preload} config.preload + * @property {IPLD} ipld + * @property {Preload} preload * * @typedef {import('..').IPLD} IPLD * @typedef {import('..').Preload} Preload From d2c013def1f6a62f139bcc11c528547083f65f40 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 13 Nov 2020 11:54:18 -0800 Subject: [PATCH 24/29] Update packages/ipfs-core/src/components/storage.js --- packages/ipfs-core/src/components/storage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ipfs-core/src/components/storage.js b/packages/ipfs-core/src/components/storage.js index 1d05d34506..4152d3bd24 100644 --- a/packages/ipfs-core/src/components/storage.js +++ b/packages/ipfs-core/src/components/storage.js @@ -61,7 +61,7 @@ const loadRepo = async (repo, options) => { return { ...await configureRepo(repo, options), isNew: false } } else if (openError.code === ERR_REPO_NOT_INITIALIZED) { if (options.allowNew === false) { - throw new NotEnabledError('new repo initialization is not enabled') + throw new NotEnabledError('Initialization of new repos disabled by config, pass `config.init.isNew: true` to enable it') } else { // If failed to open, because repo isn't initilaized and initalizing a // new repo allowed, init repo: From a857bb7d11d7a255beb5d5d20b598e4be9fda5d9 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 25 Nov 2020 10:27:47 -0800 Subject: [PATCH 25/29] chore: update git urls --- examples/browser-ipns-publish/package.json | 2 +- examples/explore-ethereum-blockchain/package.json | 2 +- examples/http-client-browser-pubsub/package.json | 2 +- examples/http-client-bundle-webpack/package.json | 2 +- examples/http-client-name-api/package.json | 2 +- packages/ipfs-core/package.json | 2 +- packages/ipfs-http-client/package.json | 2 +- packages/ipfs/package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json index 96c00fdb00..7a2193f0fa 100644 --- a/examples/browser-ipns-publish/package.json +++ b/examples/browser-ipns-publish/package.json @@ -27,7 +27,7 @@ "devDependencies": { "delay": "^4.4.0", "execa": "^4.0.3", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "go-ipfs": "^0.7.0", "parcel-bundler": "^1.12.4", "path": "^0.12.7", diff --git a/examples/explore-ethereum-blockchain/package.json b/examples/explore-ethereum-blockchain/package.json index 952a995e77..919af051e6 100644 --- a/examples/explore-ethereum-blockchain/package.json +++ b/examples/explore-ethereum-blockchain/package.json @@ -12,7 +12,7 @@ "devDependencies": { "ipfs": "^0.52.1", "ipfs-http-client": "^48.1.1", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "ipld-ethereum": "^5.0.1", "test-ipfs-example": "^2.0.3" } diff --git a/examples/http-client-browser-pubsub/package.json b/examples/http-client-browser-pubsub/package.json index 9aac4c37fc..8c8ec9f1d6 100644 --- a/examples/http-client-browser-pubsub/package.json +++ b/examples/http-client-browser-pubsub/package.json @@ -21,7 +21,7 @@ "execa": "^4.0.3", "go-ipfs": "^0.7.0", "ipfs": "^0.52.1", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "parcel-bundler": "^1.12.4", "test-ipfs-example": "^2.0.3" } diff --git a/examples/http-client-bundle-webpack/package.json b/examples/http-client-bundle-webpack/package.json index d082d46ebb..b617f43cd9 100644 --- a/examples/http-client-bundle-webpack/package.json +++ b/examples/http-client-bundle-webpack/package.json @@ -25,7 +25,7 @@ "copy-webpack-plugin": "^5.0.4", "execa": "^4.0.3", "ipfs": "^0.52.1", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "react-hot-loader": "^4.12.21", "rimraf": "^3.0.2", "test-ipfs-example": "^2.0.3", diff --git a/examples/http-client-name-api/package.json b/examples/http-client-name-api/package.json index 0d417af28c..8beda603fe 100644 --- a/examples/http-client-name-api/package.json +++ b/examples/http-client-name-api/package.json @@ -18,7 +18,7 @@ "devDependencies": { "execa": "^4.0.3", "go-ipfs": "^0.7.0", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "parcel-bundler": "^1.12.4", "rimraf": "^3.0.2", "test-ipfs-example": "^2.0.3" diff --git a/packages/ipfs-core/package.json b/packages/ipfs-core/package.json index 48a679a88d..3558d10df1 100644 --- a/packages/ipfs-core/package.json +++ b/packages/ipfs-core/package.json @@ -122,7 +122,7 @@ "delay": "^4.4.0", "go-ipfs": "^0.7.0", "interface-ipfs-core": "^0.142.2", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "ipld-git": "^0.6.1", "iso-url": "^1.0.0", "nanoid": "^3.1.12", diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index 45af0b977a..ecac9b0ae3 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -80,7 +80,7 @@ "aegir": "^28.2.0", "go-ipfs": "^0.7.0", "ipfs-core": "^0.3.0", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "it-all": "^1.0.4", "it-concat": "^1.0.1", "nock": "^13.0.2", diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index 04f8b3c756..c751f00025 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -53,7 +53,7 @@ "ipfs-http-client": "^48.1.2", "ipfs-interop": "^3.0.0", "ipfs-utils": "^5.0.0", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl#chore/without-init", + "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", "iso-url": "^1.0.0", "libp2p-webrtc-star": "^0.20.1", "merge-options": "^2.0.0", From 8d736ab685ec11965725d23040b5310674586751 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 25 Nov 2020 22:49:46 -0800 Subject: [PATCH 26/29] fix: types for withTimeoutOptions --- packages/ipfs-core-utils/src/index.js | 6 ------ packages/ipfs-core-utils/src/with-timeout-option.js | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/ipfs-core-utils/src/index.js b/packages/ipfs-core-utils/src/index.js index 89aa433f85..ccacec309b 100644 --- a/packages/ipfs-core-utils/src/index.js +++ b/packages/ipfs-core-utils/src/index.js @@ -1,7 +1 @@ 'use strict' - -/** - * @template {any[]} ARGS - * @template R - * @typedef {(...args: ARGS) => R} Fn - */ diff --git a/packages/ipfs-core-utils/src/with-timeout-option.js b/packages/ipfs-core-utils/src/with-timeout-option.js index ace39cccce..7e20556bfd 100644 --- a/packages/ipfs-core-utils/src/with-timeout-option.js +++ b/packages/ipfs-core-utils/src/with-timeout-option.js @@ -7,15 +7,15 @@ const parseDuration = require('parse-duration').default const { TimeoutError } = require('./errors') /** - * @template {any[]} ARGS + * @template {any[]} Args * @template {Promise | AsyncIterable} R - The return type of `fn` - * @param {Fn} fn + * @param {(...args:Args) => R} fn * @param {number} [optionsArgIndex] - * @returns {Fn} + * @returns {(...args:Args) => R} */ function withTimeoutOption (fn, optionsArgIndex) { // eslint-disable-next-line - return /** @returns {R} */(/** @type {ARGS} */...args) => { + return /** @returns {R} */(/** @type {Args} */...args) => { const options = args[optionsArgIndex == null ? args.length - 1 : optionsArgIndex] if (!options || !options.timeout) return fn(...args) From 7fe8d7619e8d27b697fb8d29cf889767f74be8fc Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 25 Nov 2020 22:51:05 -0800 Subject: [PATCH 27/29] fix: regressions introduced by a merge --- packages/ipfs-core/src/components/block/put.js | 14 +++++++++----- packages/ipfs-core/src/components/dht.js | 1 + packages/ipfs-core/src/components/index.js | 3 ++- packages/ipfs-core/src/components/key/index.js | 4 ++++ .../ipfs-core/src/components/pin/pin-manager.js | 2 +- packages/ipfs-http-client/src/block/put.js | 2 +- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/ipfs-core/src/components/block/put.js b/packages/ipfs-core/src/components/block/put.js index eb677e9fa4..9d3aab45c1 100644 --- a/packages/ipfs-core/src/components/block/put.js +++ b/packages/ipfs-core/src/components/block/put.js @@ -21,9 +21,6 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { * don't need to pass options, as the block instance will carry the CID * value as a property. * - * @param {IPLDBlock} block - The block or data to store - * @param {PutOptions & AbortOptions} [options] - **Note:** If you pass a `Block` instance as the block parameter, you don't need to pass options, as the block instance will carry the CID value as a property. - * @returns {Promise} - A Block type object, containing both the data and the hash of the block * @example * ```js * // Defaults @@ -53,6 +50,10 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { * // Logs: * // the CID of the object * ``` + * + * @param {IPLDBlock|Uint8Array} block - The block or data to store + * @param {PutOptions & AbortOptions} [options] - **Note:** If you pass a `Block` instance as the block parameter, you don't need to pass options, as the block instance will carry the CID value as a property. + * @returns {Promise} - A Block type object, containing both the data and the hash of the block */ async function put (block, options = {}) { if (Array.isArray(block)) { @@ -60,8 +61,11 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { } if (!Block.isBlock(block)) { + /** @type {Uint8Array} */ + const bytes = (block) if (options.cid && isIPFS.cid(options.cid)) { - block = new Block(block, CID.isCID(options.cid) ? options.cid : new CID(options.cid)) + const cid = CID.isCID(options.cid) ? options.cid : new CID(options.cid) + block = new Block(bytes, cid) } else { const mhtype = options.mhtype || 'sha2-256' const format = options.format || 'dag-pb' @@ -81,7 +85,7 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { const multihash = await multihashing(block, mhtype) const cid = new CID(cidVersion, format, multihash) - block = new Block(block, cid) + block = new Block(bytes, cid) } } diff --git a/packages/ipfs-core/src/components/dht.js b/packages/ipfs-core/src/components/dht.js index 08ade148c4..f13ba3b8b7 100644 --- a/packages/ipfs-core/src/components/dht.js +++ b/packages/ipfs-core/src/components/dht.js @@ -4,6 +4,7 @@ const PeerId = require('peer-id') const CID = require('cids') const errCode = require('err-code') const { NotEnabledError } = require('../errors') +const get = require('dlv') const withTimeoutOption = require('ipfs-core-utils/src/with-timeout-option') /** diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 13171170fa..f2d8b06759 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -313,8 +313,9 @@ const getDefaultOptions = () => ({ * @property {IPLDOptions} [ipld] - Modify the default IPLD config. This object * will be *merged* with the default config; it will not replace it. Check IPLD * [docs](https://github.com/ipld/js-ipld#ipld-constructor) for more information - * on the available options. (Default: [`ipld-nodejs.js`] + * on the available options. (Default: [`ipld.js`] * (https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld-nodejs.js) in Node.js, [`ipld-browser.js`](https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld-browser.js) + * (https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs/src/core/runtime/ipld.js) * in browsers) * @property {LibP2POptions|Function} [libp2p] - The libp2p option allows you to build * your libp2p node by configuration, or via a bundle function. If you are diff --git a/packages/ipfs-core/src/components/key/index.js b/packages/ipfs-core/src/components/key/index.js index 586cdfd720..4869addc74 100644 --- a/packages/ipfs-core/src/components/key/index.js +++ b/packages/ipfs-core/src/components/key/index.js @@ -28,4 +28,8 @@ module.exports = KeyAPI /** * @typedef {import('..').Keychain} Keychain * @typedef {import('..').AbortOptions} AbortOptions + * + * @typedef {Object} Key + * @property {string} name + * @property {string} id */ diff --git a/packages/ipfs-core/src/components/pin/pin-manager.js b/packages/ipfs-core/src/components/pin/pin-manager.js index 53f16f119f..e284831ec2 100644 --- a/packages/ipfs-core/src/components/pin/pin-manager.js +++ b/packages/ipfs-core/src/components/pin/pin-manager.js @@ -65,7 +65,7 @@ class PinManager { /** * @private * @param {CID} cid - * @param {Object} [options] + * @param {Object} options * @param {boolean} [options.preload] */ async * _walkDag (cid, { preload = false }) { diff --git a/packages/ipfs-http-client/src/block/put.js b/packages/ipfs-http-client/src/block/put.js index 6548534b2d..bf30422c74 100644 --- a/packages/ipfs-http-client/src/block/put.js +++ b/packages/ipfs-http-client/src/block/put.js @@ -66,7 +66,7 @@ module.exports = configure(api => { throw err } - return new Block(data, new CID(res.Key)) + return new Block(/** @type {Uint8Array} */(data), new CID(res.Key)) } return put From e67525ff402bc6e14f1baacb4d0aa5d61ea0511b Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 27 Nov 2020 07:24:58 -0800 Subject: [PATCH 28/29] chore: remove unused p-defer --- packages/ipfs-core/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ipfs-core/package.json b/packages/ipfs-core/package.json index 3558d10df1..a4075d89d2 100644 --- a/packages/ipfs-core/package.json +++ b/packages/ipfs-core/package.json @@ -110,7 +110,6 @@ "multicodec": "^2.0.1", "multihashing-async": "^2.0.1", "native-abort-controller": "~0.0.3", - "p-defer": "^3.0.0", "p-queue": "^6.6.1", "parse-duration": "^0.4.4", "peer-id": "^0.14.1", From d1e3e027ae1310e4878e91185248cd0face66478 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Fri, 4 Dec 2020 16:36:09 +0000 Subject: [PATCH 29/29] chore: remove gh url --- examples/browser-ipns-publish/package.json | 2 +- examples/explore-ethereum-blockchain/package.json | 2 +- examples/http-client-browser-pubsub/package.json | 2 +- examples/http-client-bundle-webpack/package.json | 2 +- examples/http-client-name-api/package.json | 2 +- packages/ipfs-core/package.json | 2 +- packages/ipfs-http-client/package.json | 2 +- packages/ipfs/package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json index 7a2193f0fa..fbdc1f0029 100644 --- a/examples/browser-ipns-publish/package.json +++ b/examples/browser-ipns-publish/package.json @@ -27,7 +27,7 @@ "devDependencies": { "delay": "^4.4.0", "execa": "^4.0.3", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "go-ipfs": "^0.7.0", "parcel-bundler": "^1.12.4", "path": "^0.12.7", diff --git a/examples/explore-ethereum-blockchain/package.json b/examples/explore-ethereum-blockchain/package.json index 919af051e6..097783c2f0 100644 --- a/examples/explore-ethereum-blockchain/package.json +++ b/examples/explore-ethereum-blockchain/package.json @@ -12,7 +12,7 @@ "devDependencies": { "ipfs": "^0.52.1", "ipfs-http-client": "^48.1.1", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "ipld-ethereum": "^5.0.1", "test-ipfs-example": "^2.0.3" } diff --git a/examples/http-client-browser-pubsub/package.json b/examples/http-client-browser-pubsub/package.json index 8c8ec9f1d6..36b9c8b1fd 100644 --- a/examples/http-client-browser-pubsub/package.json +++ b/examples/http-client-browser-pubsub/package.json @@ -21,7 +21,7 @@ "execa": "^4.0.3", "go-ipfs": "^0.7.0", "ipfs": "^0.52.1", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "parcel-bundler": "^1.12.4", "test-ipfs-example": "^2.0.3" } diff --git a/examples/http-client-bundle-webpack/package.json b/examples/http-client-bundle-webpack/package.json index b617f43cd9..6d9b00b922 100644 --- a/examples/http-client-bundle-webpack/package.json +++ b/examples/http-client-bundle-webpack/package.json @@ -25,7 +25,7 @@ "copy-webpack-plugin": "^5.0.4", "execa": "^4.0.3", "ipfs": "^0.52.1", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "react-hot-loader": "^4.12.21", "rimraf": "^3.0.2", "test-ipfs-example": "^2.0.3", diff --git a/examples/http-client-name-api/package.json b/examples/http-client-name-api/package.json index 8beda603fe..90e0c1ab0c 100644 --- a/examples/http-client-name-api/package.json +++ b/examples/http-client-name-api/package.json @@ -18,7 +18,7 @@ "devDependencies": { "execa": "^4.0.3", "go-ipfs": "^0.7.0", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "parcel-bundler": "^1.12.4", "rimraf": "^3.0.2", "test-ipfs-example": "^2.0.3" diff --git a/packages/ipfs-core/package.json b/packages/ipfs-core/package.json index 5521ec941e..25693c3e28 100644 --- a/packages/ipfs-core/package.json +++ b/packages/ipfs-core/package.json @@ -121,7 +121,7 @@ "delay": "^4.4.0", "go-ipfs": "^0.7.0", "interface-ipfs-core": "^0.142.2", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "ipld-git": "^0.6.1", "iso-url": "^1.0.0", "nanoid": "^3.1.12", diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index ecac9b0ae3..509e2e7602 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -80,7 +80,7 @@ "aegir": "^28.2.0", "go-ipfs": "^0.7.0", "ipfs-core": "^0.3.0", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "it-all": "^1.0.4", "it-concat": "^1.0.1", "nock": "^13.0.2", diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index c751f00025..796d449b1d 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -53,7 +53,7 @@ "ipfs-http-client": "^48.1.2", "ipfs-interop": "^3.0.0", "ipfs-utils": "^5.0.0", - "ipfsd-ctl": "git://github.com/ipfs/js-ipfsd-ctl", + "ipfsd-ctl": "^7.1.1", "iso-url": "^1.0.0", "libp2p-webrtc-star": "^0.20.1", "merge-options": "^2.0.0",