From be26dd723ed8c76efee149a993a8ade7f75f960e Mon Sep 17 00:00:00 2001 From: Xmader Date: Thu, 3 Sep 2020 05:30:02 -0400 Subject: [PATCH] feat: add typeScript support (#3236) TypeScript support for `ipfs` and `ipfs-http-client` Refs: #2945 Refs: #1166 Co-authored-by: Irakli Gozalishvili Co-authored-by: Alex Potsides --- docs/core-api/FILES.md | 10 +-- docs/core-api/REPO.md | 2 +- packages/ipfs-http-client/package.json | 3 +- packages/ipfs-http-client/src/add-all.js | 17 +++- packages/ipfs-http-client/src/add.js | 16 +++- packages/ipfs-http-client/src/bitswap/stat.js | 7 +- .../ipfs-http-client/src/bitswap/unwant.js | 7 +- .../src/bitswap/wantlist-for-peer.js | 7 +- .../ipfs-http-client/src/bitswap/wantlist.js | 7 +- packages/ipfs-http-client/src/block/get.js | 7 +- packages/ipfs-http-client/src/block/put.js | 6 +- .../ipfs-http-client/src/config/replace.js | 2 +- packages/ipfs-http-client/src/dag/put.js | 2 +- packages/ipfs-http-client/src/files/write.js | 2 +- packages/ipfs-http-client/src/id.js | 7 +- packages/ipfs-http-client/src/index.js | 14 +++- .../ipfs-http-client/src/lib/configure.js | 15 +++- .../src/lib/to-url-search-params.js | 4 + .../src/object/patch/append-data.js | 2 +- .../src/object/patch/set-data.js | 2 +- packages/ipfs-http-client/src/object/put.js | 2 +- .../ipfs-http-client/src/pubsub/publish.js | 2 +- .../src/pubsub/subscription-tracker.js | 2 +- packages/ipfs-http-client/src/resolve.js | 7 +- packages/ipfs-http-client/src/version.js | 7 +- packages/ipfs/package.json | 3 +- packages/ipfs/src/core/api-manager.js | 24 +++++- .../ipfs/src/core/components/add-all/index.js | 57 ++++++++++++- packages/ipfs/src/core/components/add.js | 34 +++++++- .../ipfs/src/core/components/bitswap/stat.js | 33 +++++++- .../src/core/components/bitswap/unwant.js | 31 +++++-- .../components/bitswap/wantlist-for-peer.js | 24 +++++- .../src/core/components/bitswap/wantlist.js | 22 ++++- .../ipfs/src/core/components/block/get.js | 29 ++++++- .../ipfs/src/core/components/block/put.js | 37 ++++++++- packages/ipfs/src/core/components/block/rm.js | 2 +- .../ipfs/src/core/components/files/chmod.js | 2 +- packages/ipfs/src/core/components/id.js | 27 ++++++- packages/ipfs/src/core/components/index.js | 3 +- packages/ipfs/src/core/components/init.js | 6 +- packages/ipfs/src/core/components/resolve.js | 23 ++++-- packages/ipfs/src/core/components/start.js | 20 +++-- packages/ipfs/src/core/components/stop.js | 8 +- packages/ipfs/src/core/components/version.js | 27 ++++++- packages/ipfs/src/core/index.js | 80 ++++++++++++++++--- packages/ipfs/src/core/preload.js | 15 ++-- packages/ipfs/src/core/utils.js | 24 +++++- packages/ipfs/src/index.js | 5 ++ 48 files changed, 596 insertions(+), 99 deletions(-) diff --git a/docs/core-api/FILES.md b/docs/core-api/FILES.md index b2611a023e..9dc726d8ff 100644 --- a/docs/core-api/FILES.md +++ b/docs/core-api/FILES.md @@ -143,7 +143,7 @@ One of `path` or `content` _must_ be passed. `FileContent` is one of the following types: ```js -Uint8Array | Blob | String | Iterable | AsyncIterable | ReadableStream +Uint8Array | Blob | String | Iterable | Iterable | AsyncIterable | ReadableStream ``` `UnixTime` is one of the following types: @@ -162,7 +162,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | -| chunker | `String` | `'size-262144` | chunking algorithm used to build ipfs DAGs | +| chunker | `String` | `'size-262144'` | chunking algorithm used to build ipfs DAGs | | cidVersion | `Number` | `0` | the CID version to use when storing the data | | hashAlg | `String` | `'sha2-256'` | multihash hashing algorithm to use | | onlyHash | `boolean` | `false` | If true, will not add blocks to the blockstore | @@ -178,7 +178,7 @@ An optional object which may have the following keys: | Type | Description | | -------- | -------- | -| `UnixFSEntry` | A object describing the added data | +| `Promise` | A object describing the added data | Each yielded object is of the form: @@ -226,7 +226,7 @@ Now [ipfs.io/ipfs/Qm..pg/myfile.txt](https://ipfs.io/ipfs/QmWXdjNC362aPDtwHPUE9o | Name | Type | Description | | ---- | ---- | ----------- | -| source | [FileStream](#filestream) | Data to import (see below) | +| source | [FileStream](#filestream) | Data to import (see below) | ##### FileStream @@ -242,7 +242,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | -| chunker | `String` | `'size-262144` | chunking algorithm used to build ipfs DAGs | +| chunker | `String` | `'size-262144'` | chunking algorithm used to build ipfs DAGs | | cidVersion | `Number` | `0` | the CID version to use when storing the data | | enableShardingExperiment | `boolean` | `false` | allows to create directories with an unlimited number of entries currently size of unixfs directories is limited by the maximum block size. Note that this is an experimental feature | | hashAlg | `String` | `'sha2-256'` | multihash hashing algorithm to use | diff --git a/docs/core-api/REPO.md b/docs/core-api/REPO.md index ad79bf8484..a545c9a14e 100644 --- a/docs/core-api/REPO.md +++ b/docs/core-api/REPO.md @@ -68,7 +68,7 @@ An optional object which may have the following keys: | Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | -| options | `boolean` | `false` | Return storage numbers in `MiB` | +| human | `boolean` | `false` | Return storage numbers in `MiB` | | timeout | `Number` | `undefined` | A timeout in ms | | signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call | diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index 06a29f0337..bf85308b42 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -185,6 +185,7 @@ "shunkin ", "victorbjelkholm ", "Łukasz Magiera ", - "Łukasz Magiera " + "Łukasz Magiera ", + "Xmader " ] } diff --git a/packages/ipfs-http-client/src/add-all.js b/packages/ipfs-http-client/src/add-all.js index de3483648d..68088db5c0 100644 --- a/packages/ipfs-http-client/src/add-all.js +++ b/packages/ipfs-http-client/src/add-all.js @@ -6,10 +6,14 @@ const configure = require('./lib/configure') const multipartRequest = require('./lib/multipart-request') const toUrlSearchParams = require('./lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure((api) => { - return async function * addAll (input, options = {}) { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../ipfs/src/core/components/add-all').AddAll} + */ + async function * addAll (input, options = {}) { const progressFn = options.progress // allow aborting requests on body errors @@ -39,8 +43,16 @@ module.exports = configure((api) => { } } } + return addAll }) +/** + * @typedef {import('../../ipfs/src/core/components/add-all').UnixFSEntry} UnixFSEntry + */ + +/** + * @returns {UnixFSEntry} + */ function toCoreInterface ({ name, hash, size, mode, mtime, mtimeNsecs }) { const output = { path: name, @@ -59,5 +71,6 @@ function toCoreInterface ({ name, hash, size, mode, mtime, mtimeNsecs }) { } } + // @ts-ignore return output } diff --git a/packages/ipfs-http-client/src/add.js b/packages/ipfs-http-client/src/add.js index 3190b9a7d9..7770be5843 100644 --- a/packages/ipfs-http-client/src/add.js +++ b/packages/ipfs-http-client/src/add.js @@ -4,12 +4,26 @@ const addAll = require('./add-all') const last = require('it-last') const configure = require('./lib/configure') +/** + * @typedef {import("./lib/core").ClientOptions} ClientOptions + */ + +// eslint-disable-next-line valid-jsdoc +/** + * @param {ClientOptions} options + */ module.exports = (options) => { const all = addAll(options) return configure(() => { - return async function add (input, options = {}) { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../ipfs/src/core/components/add').Add} + */ + async function add (input, options = {}) { // eslint-disable-line require-await + // @ts-ignore return last(all(input, options)) } + return add })(options) } diff --git a/packages/ipfs-http-client/src/bitswap/stat.js b/packages/ipfs-http-client/src/bitswap/stat.js index ec27458969..8f1b64afad 100644 --- a/packages/ipfs-http-client/src/bitswap/stat.js +++ b/packages/ipfs-http-client/src/bitswap/stat.js @@ -6,7 +6,11 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../../ipfs/src/core/components/bitswap/stat').Stat} + */ + async function stat (options = {}) { const res = await api.post('bitswap/stat', { searchParams: toUrlSearchParams(options), timeout: options.timeout, @@ -16,6 +20,7 @@ module.exports = configure(api => { return toCoreInterface(await res.json()) } + return stat }) function toCoreInterface (res) { diff --git a/packages/ipfs-http-client/src/bitswap/unwant.js b/packages/ipfs-http-client/src/bitswap/unwant.js index b9706cebc4..602776e7c4 100644 --- a/packages/ipfs-http-client/src/bitswap/unwant.js +++ b/packages/ipfs-http-client/src/bitswap/unwant.js @@ -5,7 +5,11 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (cid, options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../../ipfs/src/core/components/bitswap/unwant').Unwant} + */ + async function unwant (cid, options = {}) { const res = await api.post('bitswap/unwant', { timeout: options.timeout, signal: options.signal, @@ -18,4 +22,5 @@ module.exports = configure(api => { return res.json() } + return unwant }) diff --git a/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js b/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js index b777ee7d6c..7d0599a380 100644 --- a/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js +++ b/packages/ipfs-http-client/src/bitswap/wantlist-for-peer.js @@ -5,7 +5,11 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (peerId, options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../../ipfs/src/core/components/bitswap/wantlist-for-peer').WantlistForPeer} + */ + async function wantlistForPeer (peerId, options = {}) { peerId = typeof peerId === 'string' ? peerId : new CID(peerId).toString() const res = await (await api.post('bitswap/wantlist', { @@ -20,4 +24,5 @@ module.exports = configure(api => { return (res.Keys || []).map(k => new CID(k['/'])) } + return wantlistForPeer }) diff --git a/packages/ipfs-http-client/src/bitswap/wantlist.js b/packages/ipfs-http-client/src/bitswap/wantlist.js index 2599c7ed79..96f33cad91 100644 --- a/packages/ipfs-http-client/src/bitswap/wantlist.js +++ b/packages/ipfs-http-client/src/bitswap/wantlist.js @@ -5,7 +5,11 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../../ipfs/src/core/components/bitswap/wantlist').WantlistFn} + */ + async function wantlist (options = {}) { const res = await (await api.post('bitswap/wantlist', { timeout: options.timeout, signal: options.signal, @@ -15,4 +19,5 @@ module.exports = configure(api => { return (res.Keys || []).map(k => new CID(k['/'])) } + return wantlist }) diff --git a/packages/ipfs-http-client/src/block/get.js b/packages/ipfs-http-client/src/block/get.js index f39aa6d96b..18a1fdf3ac 100644 --- a/packages/ipfs-http-client/src/block/get.js +++ b/packages/ipfs-http-client/src/block/get.js @@ -6,7 +6,11 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') module.exports = configure(api => { - return async (cid, options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../../ipfs/src/core/components/block/get').BlockGet} + */ + async function get (cid, options = {}) { cid = new CID(cid) const res = await api.post('block/get', { @@ -21,4 +25,5 @@ module.exports = configure(api => { return new Block(new Uint8Array(await res.arrayBuffer()), cid) } + return get }) diff --git a/packages/ipfs-http-client/src/block/put.js b/packages/ipfs-http-client/src/block/put.js index 09888a815e..5d3f62803a 100644 --- a/packages/ipfs-http-client/src/block/put.js +++ b/packages/ipfs-http-client/src/block/put.js @@ -7,9 +7,13 @@ const multipartRequest = require('../lib/multipart-request') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure(api => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../../ipfs/src/core/components/block/put').BlockPut} + */ async function put (data, options = {}) { if (Block.isBlock(data)) { const { name, length } = multihash.decode(data.cid.multihash) diff --git a/packages/ipfs-http-client/src/config/replace.js b/packages/ipfs-http-client/src/config/replace.js index 63ced0e20a..33df9ed5a5 100644 --- a/packages/ipfs-http-client/src/config/replace.js +++ b/packages/ipfs-http-client/src/config/replace.js @@ -5,7 +5,7 @@ const multipartRequest = require('../lib/multipart-request') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure(api => { return async (config, options = {}) => { diff --git a/packages/ipfs-http-client/src/dag/put.js b/packages/ipfs-http-client/src/dag/put.js index e4f261c632..d0a395b435 100644 --- a/packages/ipfs-http-client/src/dag/put.js +++ b/packages/ipfs-http-client/src/dag/put.js @@ -9,7 +9,7 @@ const configure = require('../lib/configure') const multipartRequest = require('../lib/multipart-request') const toUrlSearchParams = require('../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') const multicodec = require('multicodec') module.exports = configure((api, opts) => { diff --git a/packages/ipfs-http-client/src/files/write.js b/packages/ipfs-http-client/src/files/write.js index c3e72c9bb3..0fcbbec808 100644 --- a/packages/ipfs-http-client/src/files/write.js +++ b/packages/ipfs-http-client/src/files/write.js @@ -6,7 +6,7 @@ const configure = require('../lib/configure') const multipartRequest = require('../lib/multipart-request') const toUrlSearchParams = require('../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure(api => { return async (path, input, options = {}) => { diff --git a/packages/ipfs-http-client/src/id.js b/packages/ipfs-http-client/src/id.js index e868460766..a4e86eb022 100644 --- a/packages/ipfs-http-client/src/id.js +++ b/packages/ipfs-http-client/src/id.js @@ -6,7 +6,11 @@ const configure = require('./lib/configure') const toUrlSearchParams = require('./lib/to-url-search-params') module.exports = configure(api => { - return async (options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../ipfs/src/core/components/id').Id} + */ + async function id (options = {}) { const res = await api.post('id', { timeout: options.timeout, signal: options.signal, @@ -23,4 +27,5 @@ module.exports = configure(api => { return output } + return id }) diff --git a/packages/ipfs-http-client/src/index.js b/packages/ipfs-http-client/src/index.js index 3d0eeeedd0..acd27d14f8 100644 --- a/packages/ipfs-http-client/src/index.js +++ b/packages/ipfs-http-client/src/index.js @@ -14,9 +14,17 @@ const urlSource = require('ipfs-utils/src/files/url-source') */ /** - * - * @param {ClientOptions } options - * @return {Object} + * @typedef {object} HttpOptions + * @property {Headers | Record} [headers] - An object or [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) instance that can be used to set custom HTTP headers. Note that this option can also be [configured globally](#custom-headers) via the constructor options. + * @property {URLSearchParams | Record} [searchParams] - An object or [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) instance that can be used to add additional query parameters to the query string sent with each request. + * @property {object} [ipld] + * @property {any[]} [ipld.formats] - An array of additional [IPLD formats](https://github.com/ipld/interface-ipld-format) to support + * @property {(format: string) => Promise} [ipld.loadFormat] - an async function that takes the name of an [IPLD format](https://github.com/ipld/interface-ipld-format) as a string and should return the implementation of that codec + */ + +// eslint-disable-next-line valid-jsdoc +/** + * @param {ClientOptions} options */ function ipfsClient (options = {}) { return { diff --git a/packages/ipfs-http-client/src/lib/configure.js b/packages/ipfs-http-client/src/lib/configure.js index 49b2d2f5a3..6396c05d1d 100644 --- a/packages/ipfs-http-client/src/lib/configure.js +++ b/packages/ipfs-http-client/src/lib/configure.js @@ -9,8 +9,19 @@ const Client = require('./core') */ /** - * @param {function(Client, ClientOptions): void} fn - * @returns {function(ClientOptions): void} + * @template T + * @typedef {(client: Client, clientOptions: ClientOptions) => T} Fn + */ + +/** + * @template T + * @typedef {(clientOptions: ClientOptions) => T} Factory + */ + +/** + * @template T + * @param {Fn} fn + * @returns {Factory} */ const configure = (fn) => { return (options) => { diff --git a/packages/ipfs-http-client/src/lib/to-url-search-params.js b/packages/ipfs-http-client/src/lib/to-url-search-params.js index 1e0bbeb4b9..10f12d83d0 100644 --- a/packages/ipfs-http-client/src/lib/to-url-search-params.js +++ b/packages/ipfs-http-client/src/lib/to-url-search-params.js @@ -3,6 +3,10 @@ const modeToString = require('./mode-to-string') const mtimeToObject = require('./mtime-to-object') +/** + * @param {object} params + * @returns {URLSearchParams} + */ module.exports = ({ arg, searchParams, hashAlg, mtime, mode, ...options } = {}) => { if (searchParams) { options = { diff --git a/packages/ipfs-http-client/src/object/patch/append-data.js b/packages/ipfs-http-client/src/object/patch/append-data.js index c334907508..bb27ea8fce 100644 --- a/packages/ipfs-http-client/src/object/patch/append-data.js +++ b/packages/ipfs-http-client/src/object/patch/append-data.js @@ -5,7 +5,7 @@ const multipartRequest = require('../../lib/multipart-request') const configure = require('../../lib/configure') const toUrlSearchParams = require('../../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure(api => { return async (cid, data, options = {}) => { diff --git a/packages/ipfs-http-client/src/object/patch/set-data.js b/packages/ipfs-http-client/src/object/patch/set-data.js index 59c3394b52..a2aa4180f1 100644 --- a/packages/ipfs-http-client/src/object/patch/set-data.js +++ b/packages/ipfs-http-client/src/object/patch/set-data.js @@ -5,7 +5,7 @@ const multipartRequest = require('../../lib/multipart-request') const configure = require('../../lib/configure') const toUrlSearchParams = require('../../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure(api => { return async (cid, data, options = {}) => { diff --git a/packages/ipfs-http-client/src/object/put.js b/packages/ipfs-http-client/src/object/put.js index c9e46898a4..f5aa827cde 100644 --- a/packages/ipfs-http-client/src/object/put.js +++ b/packages/ipfs-http-client/src/object/put.js @@ -6,7 +6,7 @@ const multipartRequest = require('../lib/multipart-request') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') const unit8ArrayToString = require('uint8arrays/to-string') const uint8ArrayFromString = require('uint8arrays/from-string') diff --git a/packages/ipfs-http-client/src/pubsub/publish.js b/packages/ipfs-http-client/src/pubsub/publish.js index 0b24f4fc28..9ec83bc691 100644 --- a/packages/ipfs-http-client/src/pubsub/publish.js +++ b/packages/ipfs-http-client/src/pubsub/publish.js @@ -4,7 +4,7 @@ const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') const multipartRequest = require('../lib/multipart-request') const anySignal = require('any-signal') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') module.exports = configure(api => { return async (topic, data, options = {}) => { diff --git a/packages/ipfs-http-client/src/pubsub/subscription-tracker.js b/packages/ipfs-http-client/src/pubsub/subscription-tracker.js index 191f832cec..d5640bdd1a 100644 --- a/packages/ipfs-http-client/src/pubsub/subscription-tracker.js +++ b/packages/ipfs-http-client/src/pubsub/subscription-tracker.js @@ -1,6 +1,6 @@ 'use strict' -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') class SubscriptionTracker { constructor () { diff --git a/packages/ipfs-http-client/src/resolve.js b/packages/ipfs-http-client/src/resolve.js index a7635dad13..1dbac9256e 100644 --- a/packages/ipfs-http-client/src/resolve.js +++ b/packages/ipfs-http-client/src/resolve.js @@ -4,7 +4,11 @@ const configure = require('./lib/configure') const toUrlSearchParams = require('./lib/to-url-search-params') module.exports = configure(api => { - return async (path, options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../ipfs/src/core/components/resolve').Resolve} + */ + async function resolve (path, options = {}) { const res = await api.post('resolve', { timeout: options.timeout, signal: options.signal, @@ -17,4 +21,5 @@ module.exports = configure(api => { const { Path } = await res.json() return Path } + return resolve }) diff --git a/packages/ipfs-http-client/src/version.js b/packages/ipfs-http-client/src/version.js index 28c49101bd..67d1ab314b 100644 --- a/packages/ipfs-http-client/src/version.js +++ b/packages/ipfs-http-client/src/version.js @@ -5,7 +5,11 @@ const configure = require('./lib/configure') const toUrlSearchParams = require('./lib/to-url-search-params') module.exports = configure(api => { - return async (options = {}) => { + // eslint-disable-next-line valid-jsdoc + /** + * @type {import('../../ipfs/src/core/components/version').Version} + */ + async function version (options = {}) { const res = await api.post('version', { timeout: options.timeout, signal: options.signal, @@ -16,4 +20,5 @@ module.exports = configure(api => { return toCamel(data) } + return version }) diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index e5d7c2a811..9c09d3fbb3 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -345,6 +345,7 @@ "tcme ", "victorbjelkholm ", "Łukasz Magiera ", - "Максим Ильин " + "Максим Ильин ", + "Xmader " ] } diff --git a/packages/ipfs/src/core/api-manager.js b/packages/ipfs/src/core/api-manager.js index 5ccc055e98..30a2ea3ae6 100644 --- a/packages/ipfs/src/core/api-manager.js +++ b/packages/ipfs/src/core/api-manager.js @@ -1,8 +1,22 @@ '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) => { @@ -12,12 +26,18 @@ module.exports = class ApiManager { }) } + /** + * @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] }) - Object.assign(this._api, nextApi) + const api = Object.assign(this._api, nextApi) if (onUndef) this._onUndef = onUndef - return { cancel: () => this.update(prevApi, prevUndef), api: this.api } + return { cancel: () => this.update(prevApi, prevUndef), api } } } diff --git a/packages/ipfs/src/core/components/add-all/index.js b/packages/ipfs/src/core/components/add-all/index.js index f72c337439..02da2817c4 100644 --- a/packages/ipfs/src/core/components/add-all/index.js +++ b/packages/ipfs/src/core/components/add-all/index.js @@ -3,13 +3,62 @@ const importer = require('ipfs-unixfs-importer') const normaliseAddInput = require('ipfs-core-utils/src/files/normalise-input') const { parseChunkerString } = require('./utils') -const pipe = require('it-pipe') +const { pipe } = require('it-pipe') const { withTimeoutOption } = require('../../utils') +/** + * @typedef {Uint8Array | Blob | String | Iterable | Iterable | AsyncIterable | ReadableStream} FileContent + * + * @typedef {object} FileObject + * - If no path is specified, then the item will be added to the root level and will be given a name according to it's CID. + * - If no content is passed, then the item is treated as an empty directory. + * - One of path or content must be passed. + * @property {string} [path] - The path you want to the file to be accessible at from the root CID _after_ it has been added + * @property {FileContent} [content] - The contents of the file + * @property {number | string} [mode] - File mode to store the entry with (see https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation) + * @property {UnixTime} [mtime] - The modification time of the entry + * + * @typedef {FileContent | FileObject} Source + * @typedef {Iterable | AsyncIterable | ReadableStream} FileStream + * + * @typedef {Date | UnixTimeObj | [number, number]} UnixTime - As an array of numbers, it must have two elements, as per the output of [`process.hrtime()`](https://nodejs.org/dist/latest/docs/api/process.html#process_process_hrtime_time). + * + * @typedef {object} UnixTimeObj + * @property {number} secs - the number of seconds since (positive) or before (negative) the Unix Epoch began + * @property {number} [nsecs] - the number of nanoseconds since the last full second. + * + * @typedef {object} UnixFSEntry + * @property {string} path + * @property {import('cids')} cid + * @property {number} mode + * @property {UnixTimeObj} mtime + * @property {number} size + */ + +/** + * @typedef {import('../add').AddOptions & _AddAllOptions} AddAllOptions + * @typedef {object} _AddAllOptions + * @property {boolean} [enableShardingExperiment] - allows to create directories with an unlimited number of entries currently size of unixfs directories is limited by the maximum block size. Note that this is an experimental feature (default: `false`) + * @property {number} [shardSplitThreshold] - Directories with more than this number of files will be created as HAMT-sharded directories (default: `1000`) + */ + +/** + * Import multiple files and data into IPFS. + * @template {Record} ExtraOptions + * @callback AddAll + * @param {FileStream} source + * @param {AddAllOptions & import('../../utils').AbortOptions & ExtraOptions} [options] + * @returns {AsyncIterable} + */ + module.exports = ({ block, gcLock, preload, pin, options: constructorOptions }) => { const isShardingEnabled = constructorOptions.EXPERIMENTAL && constructorOptions.EXPERIMENTAL.sharding - return withTimeoutOption(async function * addAll (source, options) { + // eslint-disable-next-line valid-jsdoc + /** + * @type {AddAll<{}>} + */ + async function * addAll (source, options) { options = options || {} const opts = { @@ -58,7 +107,9 @@ module.exports = ({ block, gcLock, preload, pin, options: constructorOptions }) } finally { releaseLock() } - }) + } + + return withTimeoutOption(addAll) } function transformFile (opts) { diff --git a/packages/ipfs/src/core/components/add.js b/packages/ipfs/src/core/components/add.js index 89a240df63..21fc461cb0 100644 --- a/packages/ipfs/src/core/components/add.js +++ b/packages/ipfs/src/core/components/add.js @@ -2,8 +2,40 @@ const last = require('it-last') +/** + * @typedef {import('./add-all').Source} Source + * @typedef {import('./add-all').UnixFSEntry} UnixFSEntry + */ + +/** + * @typedef {object} AddOptions + * @property {string} [chunker] - chunking algorithm used to build ipfs DAGs (default: `'size-262144'`) + * @property {number} [cidVersion] - the CID version to use when storing the data (default: `0`) + * @property {string} [hashAlg] - multihash hashing algorithm to use (default: `'sha2-256'`) + * @property {boolean} [onlyHash] - If true, will not add blocks to the blockstore (default: `false`) + * @property {boolean} [pin] - pin this object when adding (default: `true`) + * @property {function} [progress] - a function that will be called with the byte length of chunks as a file is added to ipfs (default: `undefined`) + * @property {boolean} [rawLeaves] - if true, DAG leaves will contain raw file data and not be wrapped in a protobuf (default: `false`) + * @property {boolean} [trickle] - if true will use the [trickle DAG](https://godoc.org/github.com/ipsn/go-ipfs/gxlibs/github.com/ipfs/go-unixfs/importer/trickle) format for DAG generation (default: `false`) + * @property {boolean} [wrapWithDirectory] - Adds a wrapping node around the content (default: `false`) + */ + +/** + * Import a file or data into IPFS. + * @template {Record} ExtraOptions + * @callback Add + * @param {Source} source - Data to import + * @param {AddOptions & import('../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} + */ + module.exports = ({ addAll }) => { - return async function add (source, options) { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {Add<{}>} + */ + async function add (source, options) { // eslint-disable-line require-await return last(addAll(source, options)) } + return add } diff --git a/packages/ipfs/src/core/components/bitswap/stat.js b/packages/ipfs/src/core/components/bitswap/stat.js index ac716029e8..2b009d17fa 100644 --- a/packages/ipfs/src/core/components/bitswap/stat.js +++ b/packages/ipfs/src/core/components/bitswap/stat.js @@ -1,11 +1,36 @@ 'use strict' -const Big = require('bignumber.js') +const Big = require('bignumber.js').default const CID = require('cids') const { withTimeoutOption } = require('../../utils') +/** + * @typedef {object} BitswapStats - An object that contains information about the bitswap agent + * @property {number} provideBufLen - an integer + * @property {import('cids')[]} wantlist + * @property {string[]} 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 + */ + +/** + * Show diagnostic information on the bitswap agent. + * @template {Record} ExtraOptions + * @callback Stat + * @param {import('../../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} + */ + module.exports = ({ bitswap }) => { - return withTimeoutOption(async function stat () { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {Stat<{}>} + */ + async function stat (options) { // eslint-disable-line require-await, @typescript-eslint/no-unused-vars const snapshot = bitswap.stat().snapshot return { @@ -19,5 +44,7 @@ module.exports = ({ bitswap }) => { blocksSent: new Big(snapshot.blocksSent), dataSent: new Big(snapshot.dataSent) } - }) + } + + return withTimeoutOption(stat) } diff --git a/packages/ipfs/src/core/components/bitswap/unwant.js b/packages/ipfs/src/core/components/bitswap/unwant.js index 6845e85fd9..8eadc17461 100644 --- a/packages/ipfs/src/core/components/bitswap/unwant.js +++ b/packages/ipfs/src/core/components/bitswap/unwant.js @@ -4,18 +4,37 @@ const CID = require('cids') const errCode = require('err-code') const { withTimeoutOption } = require('../../utils') +/** + * @typedef {import('cids')} CID + */ + +/** + * Removes one or more CIDs from the wantlist + * @template {Record} ExtraOptions + * @callback Unwant + * @param {CID | CID[]} cids - The CIDs to remove from the wantlist + * @param {import('../../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} - A promise that resolves once the request is complete + */ + module.exports = ({ bitswap }) => { - return withTimeoutOption(async function unwant (keys, options) { // eslint-disable-line require-await - if (!Array.isArray(keys)) { - keys = [keys] + // eslint-disable-next-line valid-jsdoc + /** + * @type {Unwant<{}>} + */ + async function unwant (cids, options) { // eslint-disable-line require-await + if (!Array.isArray(cids)) { + cids = [cids] } try { - keys = keys.map((key) => new CID(key)) + cids = cids.map((cid) => new CID(cid)) } catch (err) { throw errCode(err, 'ERR_INVALID_CID') } - return bitswap.unwant(keys, options) - }) + return bitswap.unwant(cids, options) + } + + return withTimeoutOption(unwant) } diff --git a/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js b/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js index 4eb4092cd3..745c60bd15 100644 --- a/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js +++ b/packages/ipfs/src/core/components/bitswap/wantlist-for-peer.js @@ -3,10 +3,30 @@ const PeerId = require('peer-id') const { withTimeoutOption } = require('../../utils') +/** + * @typedef {import('cids')} CID + * @typedef {import('peer-id')} PeerId + */ + +/** + * Returns the wantlist for a connected peer + * @template {Record} ExtraOptions + * @callback WantlistForPeer + * @param {PeerId | CID | string | Buffer} peerId - A peer ID to return the wantlist for\ + * @param {import('../../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} - An array of CIDs currently in the wantlist + */ + module.exports = ({ bitswap }) => { - return withTimeoutOption(async function wantlistForPeer (peerId, options = {}) { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {WantlistForPeer<{}>} + */ + async function wantlistForPeer (peerId, options = {}) { // eslint-disable-line require-await const list = bitswap.wantlistForPeer(PeerId.createFromCID(peerId), options) return Array.from(list).map(e => e[1].cid) - }) + } + + return withTimeoutOption(wantlistForPeer) } diff --git a/packages/ipfs/src/core/components/bitswap/wantlist.js b/packages/ipfs/src/core/components/bitswap/wantlist.js index 13930dd166..4347df7760 100644 --- a/packages/ipfs/src/core/components/bitswap/wantlist.js +++ b/packages/ipfs/src/core/components/bitswap/wantlist.js @@ -2,10 +2,28 @@ const { withTimeoutOption } = require('../../utils') +/** + * @typedef {import('cids')} CID + */ + +/** + * Returns the wantlist for your node + * @template {Record} ExtraOptions + * @callback WantlistFn + * @param {import('../../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} - An array of CIDs currently in the wantlist + */ + module.exports = ({ bitswap }) => { - return withTimeoutOption(async function wantlist (options = {}) { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {WantlistFn<{}>} + */ + async function wantlist (options = {}) { // eslint-disable-line require-await const list = bitswap.getWantlist(options) return Array.from(list).map(e => e[1].cid) - }) + } + + return withTimeoutOption(wantlist) } diff --git a/packages/ipfs/src/core/components/block/get.js b/packages/ipfs/src/core/components/block/get.js index 48e17c9af6..5a2c12e879 100644 --- a/packages/ipfs/src/core/components/block/get.js +++ b/packages/ipfs/src/core/components/block/get.js @@ -3,8 +3,31 @@ const { cleanCid } = require('./utils') const { withTimeoutOption } = require('../../utils') +/** + * @typedef {import('cids')} CID + * @typedef {import('ipld-block')} Block + */ + +/** + * @typedef {object} PreloadOptions + * @property {boolean} [preload] - (default: `true`) + */ + +/** + * Get a raw IPFS block. + * @template {Record} ExtraOptions + * @callback BlockGet + * @param {CID | string | Buffer} cid - A CID that corresponds to the desired block + * @param {import('../../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} - A Block type object, containing both the data and the hash of the block + */ + module.exports = ({ blockService, preload }) => { - return withTimeoutOption(async function get (cid, options) { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {BlockGet} + */ + async function get (cid, options) { // eslint-disable-line require-await options = options || {} cid = cleanCid(cid) @@ -13,5 +36,7 @@ module.exports = ({ blockService, preload }) => { } return blockService.get(cid, options) - }) + } + + return withTimeoutOption(get) } diff --git a/packages/ipfs/src/core/components/block/put.js b/packages/ipfs/src/core/components/block/put.js index 6d41c7bdd4..700b256730 100644 --- a/packages/ipfs/src/core/components/block/put.js +++ b/packages/ipfs/src/core/components/block/put.js @@ -6,8 +6,37 @@ const CID = require('cids') const isIPFS = require('is-ipfs') const { withTimeoutOption } = require('../../utils') +/** + * @typedef {import('cids')} CID + * @typedef {import('ipld-block')} Block + * @typedef {0 | 1} CidVersion + */ + +/** + * @typedef {object} BlockPutOptions + * @property {CID} [cid] - A CID to store the block under (default: `undefined`) + * @property {string} [format] - The codec to use to create the CID (default: `'dag-pb'`) + * @property {string} [mhtype] - The hashing algorithm to use to create the CID (default: `'sha2-256'`) + * @property {number} [mhlen] + * @property {CidVersion} [version] - The version to use to create the CID (default: `0`) + * @property {boolean} [pin] - If true, pin added blocks recursively (default: `false`) + */ + +/** + * Stores input as an IPFS block. + * @template {Record} ExtraOptions + * @callback BlockPut + * @param {Buffer | Block} block - The block or data to store + * @param {BlockPutOptions & import('../../utils').AbortOptions & ExtraOptions} [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 + */ + module.exports = ({ blockService, pin, gcLock, preload }) => { - return withTimeoutOption(async function put (block, options) { + // eslint-disable-next-line valid-jsdoc + /** + * @type {BlockPut} + */ + async function put (block, options) { options = options || {} if (Array.isArray(block)) { @@ -20,6 +49,8 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { } else { const mhtype = options.mhtype || 'sha2-256' const format = options.format || 'dag-pb' + + /** @type {CidVersion} */ let cidVersion if (options.version == null) { @@ -58,5 +89,7 @@ module.exports = ({ blockService, pin, gcLock, preload }) => { } finally { release() } - }) + } + + return withTimeoutOption(put) } diff --git a/packages/ipfs/src/core/components/block/rm.js b/packages/ipfs/src/core/components/block/rm.js index 406529f9be..1f7e1e6315 100644 --- a/packages/ipfs/src/core/components/block/rm.js +++ b/packages/ipfs/src/core/components/block/rm.js @@ -3,7 +3,7 @@ const CID = require('cids') const errCode = require('err-code') const { parallelMap, filter } = require('streaming-iterables') -const pipe = require('it-pipe') +const { pipe } = require('it-pipe') const { PinTypes } = require('../pin/pin-manager') const { cleanCid } = require('./utils') const { withTimeoutOption } = require('../../utils') diff --git a/packages/ipfs/src/core/components/files/chmod.js b/packages/ipfs/src/core/components/files/chmod.js index ef9838acb9..c814b11f5c 100644 --- a/packages/ipfs/src/core/components/files/chmod.js +++ b/packages/ipfs/src/core/components/files/chmod.js @@ -12,7 +12,7 @@ const updateMfsRoot = require('./utils/update-mfs-root') const { DAGNode } = require('ipld-dag-pb') const mc = require('multicodec') const mh = require('multihashing-async').multihash -const pipe = require('it-pipe') +const { pipe } = require('it-pipe') const importer = require('ipfs-unixfs-importer') const exporter = require('ipfs-unixfs-exporter') const last = require('it-last') diff --git a/packages/ipfs/src/core/components/id.js b/packages/ipfs/src/core/components/id.js index 29dd07ff2d..bebdd86379 100644 --- a/packages/ipfs/src/core/components/id.js +++ b/packages/ipfs/src/core/components/id.js @@ -5,8 +5,30 @@ const multiaddr = require('multiaddr') const { withTimeoutOption } = require('../utils') const uint8ArrayToString = require('uint8arrays/to-string') +/** + * @typedef {object} PeerIdObj - An object with the Peer identity + * @property {string} id - the Peer ID + * @property {string} publicKey - the public key of the peer as a base64 encoded string + * @property {import('multiaddr')[]} addresses - A list of multiaddrs this node is listening on + * @property {string} agentVersion - The agent version + * @property {string} protocolVersion - The supported protocol version + * @property {string[]} protocols - The supported protocols + */ + +/** + * Returns the identity of the Peer + * @template {Record} ExtraOptions + * @callback Id + * @param {import('../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} + */ + module.exports = ({ peerId, libp2p }) => { - return withTimeoutOption(async function id () { // eslint-disable-line require-await + // eslint-disable-next-line valid-jsdoc + /** + * @type {Id<{}>} + */ + async function id (options) { // eslint-disable-line require-await, @typescript-eslint/no-unused-vars const id = peerId.toB58String() let addresses = [] let protocols = [] @@ -38,5 +60,6 @@ module.exports = ({ peerId, libp2p }) => { protocolVersion: '9000', protocols: protocols.sort() } - }) + } + return withTimeoutOption(id) } diff --git a/packages/ipfs/src/core/components/index.js b/packages/ipfs/src/core/components/index.js index 8d2abb01b2..9911775886 100644 --- a/packages/ipfs/src/core/components/index.js +++ b/packages/ipfs/src/core/components/index.js @@ -79,8 +79,7 @@ exports.pin = { } exports.ping = require('./ping') exports.pubsub = require('./pubsub') -exports.refs = require('./refs') -exports.refs.local = require('./refs/local') +exports.refs = Object.assign(require('./refs'), { local: require('./refs/local') }) exports.repo = { gc: require('./repo/gc'), stat: require('./repo/stat'), diff --git a/packages/ipfs/src/core/components/init.js b/packages/ipfs/src/core/components/init.js index c5cd008fbb..d6963091a7 100644 --- a/packages/ipfs/src/core/components/init.js +++ b/packages/ipfs/src/core/components/init.js @@ -170,12 +170,14 @@ module.exports = ({ }) apiManager.update(api, () => { throw new NotStartedError() }) + + /** @type {typeof api} */ + const initializedApi = apiManager.api + return initializedApi } catch (err) { cancel() throw err } - - return apiManager.api } async function initNewRepo (repo, { privateKey, emptyRepo, algorithm, bits, profiles, config, pass, print }) { diff --git a/packages/ipfs/src/core/components/resolve.js b/packages/ipfs/src/core/components/resolve.js index b55a375ec8..b56d381140 100644 --- a/packages/ipfs/src/core/components/resolve.js +++ b/packages/ipfs/src/core/components/resolve.js @@ -6,21 +6,30 @@ const { cidToString } = require('../../utils/cid') const { withTimeoutOption } = require('../utils') /** - * @typedef {Object} ResolveOptions - * @prop {string} cidBase - Multibase codec name the CID in the resolved path will be encoded with - * @prop {boolean} [recursive=true] - Resolve until the result is an IPFS name - * + * @typedef {object} ResolveOptions + * @property {string} [cidBase='base58btc'] - Multibase codec name the CID in the resolved path will be encoded with + * @property {boolean} [recursive=true] - Resolve until the result is an IPFS name */ -/** @typedef {(path: string, options?: ResolveOptions) => Promise} Resolve */ +/** + * Resolve the value of names to IPFS + * + * There are a number of mutable name protocols that can link among themselves and into IPNS. For example IPNS references can (currently) point at an IPFS object, and DNS links can point at other DNS links, IPNS entries, or IPFS objects. This command accepts any of these identifiers and resolves them to the referenced item. + * + * @template {Record} ExtraOptions + * @callback Resolve + * @param {string} path - The name to resolve + * @param {ResolveOptions & import('../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} - A string representing the resolved name + */ /** * IPFS Resolve factory * - * @param {Object} config + * @param {object} config * @param {IPLD} config.ipld - An instance of IPLD * @param {NameApi} [config.name] - An IPFS core interface name API - * @returns {Resolve} + * @returns {Resolve<{}>} */ module.exports = ({ ipld, name }) => { return withTimeoutOption(async function resolve (path, opts) { diff --git a/packages/ipfs/src/core/components/start.js b/packages/ipfs/src/core/components/start.js index b6d5fbbc6a..d0f6d2e553 100644 --- a/packages/ipfs/src/core/components/start.js +++ b/packages/ipfs/src/core/components/start.js @@ -148,16 +148,24 @@ module.exports = ({ }) apiManager.update(api, () => undefined) + + /** @type {typeof api} */ + const startedApi = apiManager.api + startPromise.resolve(startedApi) + return startedApi } catch (err) { cancel() startPromise.reject(err) throw err } - - startPromise.resolve(apiManager.api) - return apiManager.api }) +// eslint-disable-next-line valid-jsdoc +/** + * @template LIBP2P + * @template BlockAPI, DagAPI, FilesAPI, PinAPI + * @param {{ [x: string]: any; libp2p: LIBP2P; block: BlockAPI; dag: DagAPI; files: FilesAPI; pin: PinAPI; }} options + */ function createApi ({ apiManager, bitswap, @@ -227,8 +235,10 @@ function createApi ({ resolve: Components.name.resolve({ dns, ipns, peerId, isOnline, options: constructorOptions }) } const resolve = Components.resolve({ name, ipld }) - const refs = Components.refs({ ipld, resolve, preload }) - refs.local = Components.refs.local({ repo }) + 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') diff --git a/packages/ipfs/src/core/components/stop.js b/packages/ipfs/src/core/components/stop.js index deefabbf9c..ebe8494353 100644 --- a/packages/ipfs/src/core/components/stop.js +++ b/packages/ipfs/src/core/components/stop.js @@ -18,7 +18,7 @@ module.exports = ({ libp2p, mfsPreload, peerId, - pinManager, + pinManager = {}, preload, print, repo @@ -120,8 +120,10 @@ function createApi ({ const addAll = Components.addAll({ block, preload, pin, gcLock, options: constructorOptions }) const resolve = Components.resolve({ ipld }) - const refs = Components.refs({ ipld, resolve, preload }) - refs.local = Components.refs.local({ repo }) + 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() diff --git a/packages/ipfs/src/core/components/version.js b/packages/ipfs/src/core/components/version.js index 77153e8541..acffb6d727 100644 --- a/packages/ipfs/src/core/components/version.js +++ b/packages/ipfs/src/core/components/version.js @@ -3,8 +3,29 @@ const pkg = require('../../../package.json') const { withTimeoutOption } = require('../utils') +/** + * @typedef {object} VersionObj - An object with the version of the implementation, the commit and the Repo. `js-ipfs` instances will also return the version of `interface-ipfs-core` and `ipfs-http-client` supported by this node + * @property {string} version + * @property {string} repo + * @property {string} [commit] + * @property {string} [interface-ipfs-core] + * @property {string} [ipfs-http-client] + */ + +/** + * Returns the implementation version + * @template {Record} ExtraOptions + * @callback Version + * @param {import('../utils').AbortOptions & ExtraOptions} [options] + * @returns {Promise} + */ + module.exports = ({ repo }) => { - return withTimeoutOption(async function version (options) { + // eslint-disable-next-line valid-jsdoc + /** + * @type {Version<{}>} + */ + async function version (options) { const repoVersion = await repo.version.get(options) return { @@ -14,5 +35,7 @@ module.exports = ({ repo }) => { 'interface-ipfs-core': pkg.devDependencies['interface-ipfs-core'], 'ipfs-http-client': pkg.dependencies['ipfs-http-client'] } - }) + } + + return withTimeoutOption(version) } diff --git a/packages/ipfs/src/core/index.js b/packages/ipfs/src/core/index.js index d13df9ed85..f111801e58 100644 --- a/packages/ipfs/src/core/index.js +++ b/packages/ipfs/src/core/index.js @@ -33,6 +33,63 @@ const getDefaultOptions = () => ({ } }) +/** + * @typedef {'rsa' | 'ed25519' | 'secp256k1'} KeyType + * + * @typedef {object} InitOptions + * @property {boolean} [emptyRepo] - Whether to remove built-in assets, like the instructional tour and empty mutable file system, from the repo. (Default: `false`) + * @property {KeyType} [algorithm] - The type of key to use. (Default: `rsa`) + * @property {number} [bits] - Number of bits to use in the generated key pair (rsa only). (Default: `2048`) + * @property {string | import('peer-id')} [privateKey] - A pre-generated private key to use. Can be either a base64 string or a [PeerId](https://github.com/libp2p/js-peer-id) instance. **NOTE: This overrides `bits`.** + * @property {string} [pass] - A passphrase to encrypt keys. You should generally use the [top-level `pass` option](#optionspass) instead of the `init.pass` option (this one will take its value from the top-level option if not set). + * @property {any[]} [profiles] - Apply profile settings to config. + * @property {boolean} [allowNew] - Set to `false` to disallow initialization if the repo does not already exist. (Default: `true`) + * + * @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('ipfs-repo') } IpfsRepo + */ + +/** + * Creates and returns a ready to use instance of an IPFS node. + * + * @template {boolean | InitOptions} INIT + * @template {boolean} START + * + * @param {object} [options] - specify advanced configuration + * @param {string | IpfsRepo} [options.repo] - 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`](https://github.com/ipfs/js-ipfs-repo) instance. (Default: `'~/.jsipfs'` in Node.js, `'ipfs'` in browsers) + * @param {boolean} [options.repoAutoMigrate] - `js-ipfs` comes bundled with a tool that automatically migrates your IPFS repository when a new version is available. (Default: `true`) + * @param {INIT} [options.init] - Perform repo initialization steps when creating the IPFS node. (Default: `true`) + * - 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. + * @param {START} [options.start] - If `false`, do not automatically start the IPFS node. Instead, you’ll need to manually call [`node.start()`](#nodestart) yourself. (Default: `true`) + * @param {string} [options.pass] - A passphrase to encrypt/decrypt your keys. (Default: `null`) + * @param {boolean} [options.silent] - Prevents all logging output from the IPFS node. (Default: `false`) + * @param {RelayOptions} [options.relay] - Configure circuit relay (see the [circuit relay tutorial](https://github.com/ipfs/js-ipfs/tree/master/examples/circuit-relaying) to learn more). (Default: `{ enabled: true, hop: { enabled: false, active: false } }`) + * @param {boolean} [options.offline] - Run ipfs node offline. The node does not connect to the rest of the network but provides a local API. (Default: `false`) + * @param {PreloadOptions} [options.preload] - Configure remote preload nodes. The remote will preload content added on this node, and also attempt to preload objects requested by this node. + * @param {ExperimentalOptions} [options.EXPERIMENTAL] - Enable and configure experimental features. + * @param {object} [options.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) + * @param {object} [options.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) + * @param {object | Function} [options.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 + */ async function create (options) { options = mergeOptions(getDefaultOptions(), options) @@ -44,20 +101,21 @@ async function create (options) { const { api } = apiManager.update({ init: Components.init({ apiManager, print, options }), dns: Components.dns(), - isOnline: Components.isOnline({}) + isOnline: Components.isOnline({ libp2p: undefined }) }, async () => { throw new NotInitializedError() }) // eslint-disable-line require-await - if (!options.init) { - return api - } - - await api.init() - - if (!options.start) { - return api - } + const initializedApi = options.init && await api.init() + const startedApi = options.start && initializedApi && await initializedApi.start() - return api.start() + /** + * @template T, THEN, ELSE + * @typedef {NonNullable extends false + * ? THEN : ELSE } IsFalse + */ + /** @type {IsFalse>} */ + // @ts-ignore + const ipfs = startedApi || initializedApi || api + return ipfs } module.exports = { diff --git a/packages/ipfs/src/core/preload.js b/packages/ipfs/src/core/preload.js index 053103c648..e763aa4d87 100644 --- a/packages/ipfs/src/core/preload.js +++ b/packages/ipfs/src/core/preload.js @@ -4,11 +4,13 @@ const toUri = require('multiaddr-to-uri') const debug = require('debug') const CID = require('cids') const shuffle = require('array-shuffle') -const AbortController = require('abort-controller') +const { AbortController } = require('abort-controller') const preload = require('./runtime/preload-nodejs') -const log = debug('ipfs:preload') -log.error = debug('ipfs:preload:error') +const log = Object.assign( + debug('ipfs:preload'), + { error: debug('ipfs:preload:error') } +) module.exports = options => { options = options || {} @@ -18,9 +20,10 @@ module.exports = options => { if (!options.enabled || !options.addresses.length) { log('preload disabled') const api = () => {} - api.start = () => {} - api.stop = () => {} - return api + return Object.assign(api, { + start: () => {}, + stop: () => {} + }) } let stopped = true diff --git a/packages/ipfs/src/core/utils.js b/packages/ipfs/src/core/utils.js index 2fd8594b37..1443ef67e7 100644 --- a/packages/ipfs/src/core/utils.js +++ b/packages/ipfs/src/core/utils.js @@ -127,8 +127,28 @@ const mapFile = (file, options) => { return output } +/** + * @template {any[]} ARGS + * @template R + * @typedef {(...args: ARGS) => R} Fn + */ + +/** + * @typedef {object} AbortOptions + * @property {number} [timeout] - A timeout in ms + * @property {AbortSignal} [signal] - Can be used to cancel any long running requests started as a result of this call + */ + +/** + * @template {any[]} ARGS + * @template {Promise | AsyncIterable} R - The return type of `fn` + * @param {Fn} fn + * @param {number} [optionsArgIndex] + * @returns {Fn} + */ function withTimeoutOption (fn, optionsArgIndex) { - return (...args) => { + // eslint-disable-next-line valid-jsdoc + return /** @returns {R} */(/** @type {ARGS} */...args) => { const options = args[optionsArgIndex == null ? args.length - 1 : optionsArgIndex] if (!options || !options.timeout) return fn(...args) @@ -166,6 +186,7 @@ function withTimeoutOption (fn, optionsArgIndex) { } if (fnRes[Symbol.asyncIterator]) { + // @ts-ignore return (async function * () { const it = fnRes[Symbol.asyncIterator]() @@ -195,6 +216,7 @@ function withTimeoutOption (fn, optionsArgIndex) { })() } + // @ts-ignore return (async () => { try { const res = await Promise.race([fnRes, timeoutPromise]) diff --git a/packages/ipfs/src/index.js b/packages/ipfs/src/index.js index 8140941bf4..cb336d3935 100644 --- a/packages/ipfs/src/index.js +++ b/packages/ipfs/src/index.js @@ -2,4 +2,9 @@ const IPFS = require('./core') +/** + * @typedef { ReturnType extends Promise + * ? U : never } IPFS + */ + module.exports = IPFS