diff --git a/.aegir.js b/.aegir.js index fec9ee097..5dcbcb339 100644 --- a/.aegir.js +++ b/.aegir.js @@ -6,7 +6,7 @@ const getPort = require('aegir/utils/get-port') /** @type {import('aegir').PartialOptions} */ module.exports = { build: { - bundlesizeMax: '89KB' + bundlesizeMax: '66KB' }, test: { async before (options) { diff --git a/package.json b/package.json index f3f260c65..00ccbac4e 100644 --- a/package.json +++ b/package.json @@ -41,42 +41,38 @@ "lint": "aegir lint", "coverage": "npx nyc -r html npm run test:node -- --bail", "clean": "rimraf ./dist", - "dep-check": "aegir dep-check -i ipfs-core -i rimraf -i ipfs-core-types -i abort-controller -i ipld" + "dep-check": "aegir dep-check -i ipfs-core -i rimraf -i ipfs-core-types -i abort-controller" }, "dependencies": { + "@ipld/dag-cbor": "^6.0.5", + "@ipld/dag-pb": "^2.1.3", "abort-controller": "^3.0.0", "any-signal": "^2.1.2", - "cids": "^1.1.6", + "err-code": "^3.0.1", "debug": "^4.1.1", "form-data": "^4.0.0", "ipfs-core-types": "^0.5.2", "ipfs-core-utils": "^0.8.3", - "ipfs-unixfs": "^4.0.3", - "ipfs-utils": "^8.1.2", - "ipld-block": "^0.11.0", - "ipld-dag-cbor": "^1.0.0", - "ipld-dag-pb": "^0.22.1", - "ipld-raw": "^7.0.0", + "ipfs-utils": "^8.1.4", + "it-first": "^1.0.6", "it-last": "^1.0.4", "it-map": "^1.0.4", "it-tar": "^3.0.0", "it-to-stream": "^1.0.0", "merge-options": "^3.0.4", - "multiaddr": "^9.0.1", - "multibase": "^4.0.2", - "multicodec": "^3.0.1", - "multihashes": "^4.0.2", + "multiaddr": "^10.0.0", + "multiformats": "^9.4.1", "nanoid": "^3.1.12", "native-abort-controller": "^1.0.3", "parse-duration": "^1.0.0", "stream-to-it": "^0.2.2", - "uint8arrays": "^2.1.3" + "uint8arrays": "^2.1.6" }, "devDependencies": { - "aegir": "^33.0.0", + "aegir": "^34.0.2", "delay": "^5.0.0", "go-ipfs": "0.8.0", - "ipfsd-ctl": "^8.0.1", + "ipfsd-ctl": "^9.0.0", "it-all": "^1.0.4", "it-concat": "^2.0.0", "it-first": "^1.0.4", diff --git a/src/add-all.js b/src/add-all.js index 4c8ca20ba..8c85b873d 100644 --- a/src/add-all.js +++ b/src/add-all.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const toCamel = require('./lib/object-to-camel') const configure = require('./lib/configure') const multipartRequest = require('./lib/multipart-request') @@ -107,13 +107,19 @@ const createOnUploadProgress = (size, parts, progress) => { } /** - * @param {any} input + * @param {object} input + * @param {string} input.name + * @param {string} input.hash + * @param {string} input.size + * @param {string} [input.mode] + * @param {number} [input.mtime] + * @param {number} [input.mtimeNsecs] */ function toCoreInterface ({ name, hash, size, mode, mtime, mtimeNsecs }) { /** @type {AddResult} */ const output = { path: name, - cid: new CID(hash), + cid: CID.parse(hash), size: parseInt(size) } diff --git a/src/bitswap/stat.js b/src/bitswap/stat.js index 0c06048ec..35d297876 100644 --- a/src/bitswap/stat.js +++ b/src/bitswap/stat.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -32,7 +32,7 @@ module.exports = configure(api => { function toCoreInterface (res) { return { provideBufLen: res.ProvideBufLen, - wantlist: (res.Wantlist || []).map((/** @type {{ '/': string }} */ k) => new CID(k['/'])), + wantlist: (res.Wantlist || []).map((/** @type {{ '/': string }} */ k) => CID.parse(k['/'])), peers: (res.Peers || []), blocksReceived: BigInt(res.BlocksReceived), dataReceived: BigInt(res.DataReceived), diff --git a/src/bitswap/unwant.js b/src/bitswap/unwant.js index 5774dd819..847b38397 100644 --- a/src/bitswap/unwant.js +++ b/src/bitswap/unwant.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -18,8 +17,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - // @ts-ignore - CID|string seems to confuse typedef - arg: typeof cid === 'string' ? cid : new CID(cid).toString(), + arg: cid.toString(), ...options }), headers: options.headers diff --git a/src/bitswap/wantlist-for-peer.js b/src/bitswap/wantlist-for-peer.js index 85fe17760..8dac0bf4d 100644 --- a/src/bitswap/wantlist-for-peer.js +++ b/src/bitswap/wantlist-for-peer.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -14,20 +14,17 @@ module.exports = configure(api => { * @type {BitswapAPI["wantlistForPeer"]} */ async function wantlistForPeer (peerId, options = {}) { - // @ts-ignore - CID|string seems to confuse typedef - peerId = typeof peerId === 'string' ? peerId : new CID(peerId).toString() - const res = await (await api.post('bitswap/wantlist', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ ...options, - peer: peerId + peer: peerId.toString() }), headers: options.headers })).json() - return (res.Keys || []).map((/** @type {{ '/': string }} */ k) => new CID(k['/'])) + return (res.Keys || []).map((/** @type {{ '/': string }} */ k) => CID.parse(k['/'])) } return wantlistForPeer }) diff --git a/src/bitswap/wantlist.js b/src/bitswap/wantlist.js index 536d43812..60d37d183 100644 --- a/src/bitswap/wantlist.js +++ b/src/bitswap/wantlist.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -21,7 +21,7 @@ module.exports = configure(api => { headers: options.headers })).json() - return (res.Keys || []).map((/** @type {{ '/': string }} */ k) => new CID(k['/'])) + return (res.Keys || []).map((/** @type {{ '/': string }} */ k) => CID.parse(k['/'])) } return wantlist }) diff --git a/src/block/get.js b/src/block/get.js index ca9d09b13..75d4f5fe7 100644 --- a/src/block/get.js +++ b/src/block/get.js @@ -1,7 +1,5 @@ 'use strict' -const Block = require('ipld-block') -const CID = require('cids') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -15,9 +13,6 @@ module.exports = configure(api => { * @type {BlockAPI["get"]} */ async function get (cid, options = {}) { - // @ts-ignore - CID|string seems to confuse typedef - cid = new CID(cid) - const res = await api.post('block/get', { timeout: options.timeout, signal: options.signal, @@ -28,7 +23,7 @@ module.exports = configure(api => { headers: options.headers }) - return new Block(new Uint8Array(await res.arrayBuffer()), cid) + return new Uint8Array(await res.arrayBuffer()) } return get }) diff --git a/src/block/put.js b/src/block/put.js index cd96ae723..57ba474db 100644 --- a/src/block/put.js +++ b/src/block/put.js @@ -1,8 +1,6 @@ 'use strict' -const Block = require('ipld-block') -const CID = require('cids') -const multihash = require('multihashes') +const { CID } = require('multiformats/cid') const multipartRequest = require('../lib/multipart-request') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -19,38 +17,12 @@ module.exports = configure(api => { * @type {BlockAPI["put"]} */ async function put (data, options = {}) { - if (Block.isBlock(data)) { - const { name, length } = multihash.decode(data.cid.multihash) - options = { - ...options, - format: data.cid.codec, - mhtype: name, - 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) - const { name, length } = multihash.decode(cid.multihash) - options = { - ...options, - format: cid.codec, - mhtype: name, - mhlen: length, - version: cid.version - } - delete options.cid - } - // allow aborting requests on body errors const controller = new AbortController() const signal = abortSignal(controller.signal, options.signal) let res try { - // @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90 const response = await api.post('block/put', { timeout: options.timeout, signal: signal, @@ -72,7 +44,7 @@ module.exports = configure(api => { throw err } - return new Block((/** @type {Uint8Array} */ data), new CID(res.Key)) + return CID.parse(res.Key) } return put diff --git a/src/block/rm.js b/src/block/rm.js index 7f9d6b0d7..18d9e17c1 100644 --- a/src/block/rm.js +++ b/src/block/rm.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -23,7 +23,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: cid.map(cid => new CID(cid).toString()), + arg: cid.map(cid => cid.toString()), 'stream-channels': true, ...options }), @@ -44,7 +44,7 @@ module.exports = configure(api => { function toCoreInterface (removed) { /** @type {RmResult} */ const out = { - cid: new CID(removed.Hash) + cid: CID.parse(removed.Hash) } if (removed.Error) { diff --git a/src/block/stat.js b/src/block/stat.js index e0c6c4bf6..8e6d14364 100644 --- a/src/block/stat.js +++ b/src/block/stat.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -18,14 +18,14 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: new CID(cid).toString(), + arg: cid.toString(), ...options }), headers: options.headers }) const data = await res.json() - return { cid: new CID(data.Key), size: data.Size } + return { cid: CID.parse(data.Key), size: data.Size } } return stat diff --git a/src/cat.js b/src/cat.js index 3ff6240f4..6d0e21fa6 100644 --- a/src/cat.js +++ b/src/cat.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const configure = require('./lib/configure') const toUrlSearchParams = require('./lib/to-url-search-params') @@ -18,7 +17,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: typeof path === 'string' ? path : new CID(path).toString(), + arg: path.toString(), ...options }), headers: options.headers diff --git a/src/dag/get.js b/src/dag/get.js index 21f30e27a..f01088194 100644 --- a/src/dag/get.js +++ b/src/dag/get.js @@ -1,35 +1,54 @@ 'use strict' const configure = require('../lib/configure') -const multicodec = require('multicodec') -const loadFormat = require('../lib/ipld-formats') +const resolve = require('../lib/resolve') +const first = require('it-first') +const last = require('it-last') +const errCode = require('err-code') /** * @typedef {import('../types').HTTPClientExtraOptions} HTTPClientExtraOptions * @typedef {import('ipfs-core-types/src/dag').API} DAGAPI */ -module.exports = configure((api, opts) => { - const getBlock = require('../block/get')(opts) - const dagResolve = require('./resolve')(opts) - const load = loadFormat(opts.ipld) - - /** - * @type {DAGAPI["get"]} - */ - const get = async (cid, options = {}) => { - const resolved = await dagResolve(cid, options) - const block = await getBlock(resolved.cid, options) - - const codecName = multicodec.getName(resolved.cid.code) - const format = await load(codecName) - - if (resolved.cid.code === multicodec.RAW && !resolved.remainderPath) { - resolved.remainderPath = '/' +/** + * @param {import('ipfs-core-utils/src/multicodecs')} codecs + * @param {import('../types').Options} options + */ +module.exports = (codecs, options) => { + const fn = configure((api, opts) => { + const getBlock = require('../block/get')(opts) + + /** + * @type {DAGAPI["get"]} + */ + const get = async (cid, options = {}) => { + if (options.path) { + const entry = options.localResolve + ? await first(resolve(cid, options.path, codecs, getBlock, options)) + : await last(resolve(cid, options.path, codecs, getBlock, options)) + /** @type {import('ipfs-core-types/src/dag').GetResult} - first and last will return undefined when empty */ + const result = (entry) + + if (!result) { + throw errCode(new Error('Not found'), 'ERR_NOT_FOUND') + } + + return result + } + + const codec = await codecs.getCodec(cid.code) + const block = await getBlock(cid, options) + const node = codec.decode(block) + + return { + value: node, + remainderPath: '' + } } - return format.resolver.resolve(block.data, resolved.remainderPath || '') - } + return get + }) - return get -}) + return fn(options) +} diff --git a/src/dag/index.js b/src/dag/index.js index 2754bb6dd..ec7f0ac5c 100644 --- a/src/dag/index.js +++ b/src/dag/index.js @@ -1,11 +1,11 @@ 'use strict' /** + * @param {import('ipfs-core-utils/src/multicodecs')} codecs * @param {import('../types').Options} config */ -module.exports = config => ({ - get: require('./get')(config), - put: require('./put')(config), - resolve: require('./resolve')(config), - tree: require('./tree')(config) +module.exports = (codecs, config) => ({ + get: require('./get')(codecs, config), + put: require('./put')(codecs, config), + resolve: require('./resolve')(config) }) diff --git a/src/dag/put.js b/src/dag/put.js index 9290b4844..49c754905 100644 --- a/src/dag/put.js +++ b/src/dag/put.js @@ -1,74 +1,57 @@ 'use strict' -const CID = require('cids') -const multihash = require('multihashes') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const multipartRequest = require('../lib/multipart-request') const toUrlSearchParams = require('../lib/to-url-search-params') const abortSignal = require('../lib/abort-signal') const { AbortController } = require('native-abort-controller') -const multicodec = require('multicodec') -const loadFormat = require('../lib/ipld-formats') /** * @typedef {import('../types').HTTPClientExtraOptions} HTTPClientExtraOptions * @typedef {import('ipfs-core-types/src/dag').API} DAGAPI */ -module.exports = configure((api, opts) => { - const load = loadFormat(opts.ipld) - - /** - * @type {DAGAPI["put"]} - */ - const put = async (dagNode, options = {}) => { - if (options.cid && (options.format || options.hashAlg)) { - throw new Error('Failed to put DAG node. Provide either `cid` OR `format` and `hashAlg` options') - } else if ((options.format && !options.hashAlg) || (!options.format && options.hashAlg)) { - throw new Error('Failed to put DAG node. Provide `format` AND `hashAlg` options') - } - - let encodingOptions - if (options.cid) { - const cid = new CID(options.cid) - encodingOptions = { - ...options, - format: multicodec.getName(cid.code), - hashAlg: multihash.decode(cid.multihash).name +/** + * @param {import('ipfs-core-utils/src/multicodecs')} codecs + * @param {import('../types').Options} options + */ +module.exports = (codecs, options) => { + const fn = configure((api) => { + /** + * @type {DAGAPI["put"]} + */ + const put = async (dagNode, options = {}) => { + const settings = { + format: 'dag-cbor', + hashAlg: 'sha2-256', + inputEnc: 'raw', + ...options } - delete options.cid - } else { - encodingOptions = options - } - const settings = { - format: 'dag-cbor', - hashAlg: 'sha2-256', - inputEnc: 'raw', - ...encodingOptions + const codec = await codecs.getCodec(settings.format) + const serialized = codec.encode(dagNode) + + // allow aborting requests on body errors + const controller = new AbortController() + const signal = abortSignal(controller.signal, settings.signal) + + // @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90 + const res = await api.post('dag/put', { + timeout: settings.timeout, + signal, + searchParams: toUrlSearchParams(settings), + ...( + await multipartRequest(serialized, controller, settings.headers) + ) + }) + const data = await res.json() + + return CID.parse(data.Cid['/']) } - // @ts-ignore settings.format might be an invalid CodecName - const format = await load(settings.format) - const serialized = format.util.serialize(dagNode) - - // allow aborting requests on body errors - const controller = new AbortController() - const signal = abortSignal(controller.signal, settings.signal) - - // @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90 - const res = await api.post('dag/put', { - timeout: settings.timeout, - signal, - searchParams: toUrlSearchParams(settings), - ...( - await multipartRequest(serialized, controller, settings.headers) - ) - }) - const data = await res.json() - - return new CID(data.Cid['/']) - } + return put + }) - return put -}) + return fn(options) +} diff --git a/src/dag/resolve.js b/src/dag/resolve.js index c0e69354b..ad2d7a58f 100644 --- a/src/dag/resolve.js +++ b/src/dag/resolve.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -26,7 +26,7 @@ module.exports = configure(api => { const data = await res.json() - return { cid: new CID(data.Cid['/']), remainderPath: data.RemPath } + return { cid: CID.parse(data.Cid['/']), remainderPath: data.RemPath } } return resolve diff --git a/src/dag/tree.js b/src/dag/tree.js deleted file mode 100644 index 43809ed84..000000000 --- a/src/dag/tree.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict' - -const configure = require('../lib/configure') - -/** - * @typedef {import('../types').HTTPClientExtraOptions} HTTPClientExtraOptions - * @typedef {import('ipfs-core-types/src/dag').API} DAGAPI - */ - -module.exports = configure(api => { - /** - * @type {DAGAPI["tree"]} - */ - const tree = async (ipfsPath, options = {}) => { - throw new Error('Not implemented') - } - - return tree -}) diff --git a/src/dht/find-provs.js b/src/dht/find-provs.js index c431648ae..91b4cd63a 100644 --- a/src/dht/find-provs.js +++ b/src/dht/find-provs.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const { Multiaddr } = require('multiaddr') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -20,7 +19,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: `${new CID(cid)}`, + arg: cid.toString(), ...options }), headers: options.headers diff --git a/src/dht/provide.js b/src/dht/provide.js index 878880398..01aedbce1 100644 --- a/src/dht/provide.js +++ b/src/dht/provide.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const { Multiaddr } = require('multiaddr') const toCamel = require('../lib/object-to-camel') const configure = require('../lib/configure') @@ -9,6 +8,7 @@ const toUrlSearchParams = require('../lib/to-url-search-params') /** * @typedef {import('../types').HTTPClientExtraOptions} HTTPClientExtraOptions * @typedef {import('ipfs-core-types/src/dht').API} DHTAPI + * @typedef {import('multiformats/cid').CID} CID */ module.exports = configure(api => { @@ -16,13 +16,14 @@ module.exports = configure(api => { * @type {DHTAPI["provide"]} */ async function * provide (cids, options = { recursive: false }) { - cids = Array.isArray(cids) ? cids : [cids] + /** @type {CID[]} */ + const cidArr = Array.isArray(cids) ? cids : [cids] const res = await api.post('dht/provide', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: cids.map(cid => new CID(cid).toString()), + arg: cidArr.map(cid => cid.toString()), ...options }), headers: options.headers @@ -30,7 +31,6 @@ module.exports = configure(api => { for await (let message of res.ndjson()) { message = toCamel(message) - message.id = new CID(message.id) if (message.responses) { message.responses = message.responses.map((/** @type {{ ID: string, Addrs: string[] }} */ { ID, Addrs }) => ({ id: ID, diff --git a/src/dht/put.js b/src/dht/put.js index d2b88aa89..1e7de6b49 100644 --- a/src/dht/put.js +++ b/src/dht/put.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const { Multiaddr } = require('multiaddr') const toCamel = require('../lib/object-to-camel') const configure = require('../lib/configure') @@ -38,7 +37,6 @@ module.exports = configure(api => { for await (let message of res.ndjson()) { message = toCamel(message) - message.id = new CID(message.id) if (message.responses) { message.responses = message.responses.map((/** @type {{ ID: string, Addrs: string[] }} */ { ID, Addrs }) => ({ id: ID, diff --git a/src/dht/query.js b/src/dht/query.js index 429f487ef..14f3326c7 100644 --- a/src/dht/query.js +++ b/src/dht/query.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const { Multiaddr } = require('multiaddr') const toCamel = require('../lib/object-to-camel') const configure = require('../lib/configure') @@ -20,7 +19,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: new CID(`${peerId}`), + arg: peerId.toString(), ...options }), headers: options.headers @@ -28,7 +27,6 @@ module.exports = configure(api => { for await (let message of res.ndjson()) { message = toCamel(message) - message.id = new CID(message.id) message.responses = (message.responses || []).map((/** @type {{ ID: string, Addrs: string[] }} */ { ID, Addrs }) => ({ id: ID, addrs: (Addrs || []).map((/** @type {string} **/ a) => new Multiaddr(a)) diff --git a/src/files/cp.js b/src/files/cp.js index f87bcef5e..4dbe59ef0 100644 --- a/src/files/cp.js +++ b/src/files/cp.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -14,15 +14,14 @@ module.exports = configure(api => { * @type {FilesAPI["cp"]} */ async function cp (sources, destination, options = {}) { - if (!Array.isArray(sources)) { - sources = [sources] - } + /** @type {import('ipfs-core-types/src/utils').IPFSPath[]} */ + const sourceArr = Array.isArray(sources) ? sources : [sources] const res = await api.post('files/cp', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: sources.concat(destination).map(src => CID.isCID(src) ? `/ipfs/${src}` : src), + arg: sourceArr.concat(destination).map(src => src instanceof CID ? `/ipfs/${src}` : src), ...options }), headers: options.headers diff --git a/src/files/flush.js b/src/files/flush.js index 33bbdae1a..0c04cc809 100644 --- a/src/files/flush.js +++ b/src/files/flush.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -29,7 +29,7 @@ module.exports = configure(api => { }) const data = await res.json() - return new CID(data.Cid) + return CID.parse(data.Cid) } return flush }) diff --git a/src/files/ls.js b/src/files/ls.js index 71d05b849..67d4b2432 100644 --- a/src/files/ls.js +++ b/src/files/ls.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -14,7 +14,7 @@ module.exports = configure(api => { * @type {FilesAPI["ls"]} */ async function * ls (path, options = {}) { - if (!path || typeof path !== 'string') { + if (!path) { throw new Error('ipfs.files.ls requires a path') } @@ -22,7 +22,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: CID.isCID(path) ? `/ipfs/${path}` : path, + arg: path instanceof CID ? `/ipfs/${path}` : path, // default long to true, diverges from go-ipfs where its false by default long: true, ...options, @@ -50,7 +50,7 @@ module.exports = configure(api => { */ function toCoreInterface (entry) { if (entry.hash) { - entry.cid = new CID(entry.hash) + entry.cid = CID.parse(entry.hash) } delete entry.hash diff --git a/src/files/mv.js b/src/files/mv.js index c8c4cf7ce..92ab4fe0f 100644 --- a/src/files/mv.js +++ b/src/files/mv.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -22,7 +21,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: sources.concat(destination).map(src => CID.isCID(src) ? `/ipfs/${src}` : src), + arg: sources.concat(destination), ...options }), headers: options.headers diff --git a/src/files/stat.js b/src/files/stat.js index da0768220..f5f678590 100644 --- a/src/files/stat.js +++ b/src/files/stat.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -15,11 +15,13 @@ module.exports = configure(api => { * @type {FilesAPI["stat"]} */ async function stat (path, options = {}) { - if (path && !CID.isCID(path) && typeof path !== 'string') { + if (path && !(path instanceof CID) && typeof path !== 'string') { options = path || {} path = '/' } + options = options || {} + const res = await api.post('files/stat', { timeout: options.timeout, signal: options.signal, @@ -41,7 +43,7 @@ module.exports = configure(api => { * @param {*} entry */ function toCoreInterface (entry) { - entry.cid = new CID(entry.hash) + entry.cid = CID.parse(entry.hash) delete entry.hash return entry } diff --git a/src/files/write.js b/src/files/write.js index f55347828..0f88fe133 100644 --- a/src/files/write.js +++ b/src/files/write.js @@ -1,7 +1,7 @@ 'use strict' const modeToString = require('../lib/mode-to-string') -const { parseMtime } = require('ipfs-unixfs') +const parseMtime = require('../lib/parse-mtime') const configure = require('../lib/configure') const multipartRequest = require('../lib/multipart-request') const toUrlSearchParams = require('../lib/to-url-search-params') diff --git a/src/get.js b/src/get.js index 5573c85c2..a83628536 100644 --- a/src/get.js +++ b/src/get.js @@ -2,7 +2,7 @@ // @ts-ignore no types const Tar = require('it-tar') -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('./lib/configure') const toUrlSearchParams = require('./lib/to-url-search-params') const map = require('it-map') @@ -21,7 +21,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: `${path instanceof Uint8Array ? new CID(path) : path}`, + arg: `${path instanceof Uint8Array ? CID.decode(path) : path}`, ...options }), headers: options.headers diff --git a/src/index.js b/src/index.js index c9c9551b2..562b0a794 100644 --- a/src/index.js +++ b/src/index.js @@ -1,23 +1,54 @@ 'use strict' /* eslint-env browser */ -const CID = require('cids') +const { CID } = require('multiformats/cid') const { multiaddr } = require('multiaddr') -const multibase = require('multibase') -const multicodec = require('multicodec') -const multihash = require('multihashes') const globSource = require('ipfs-utils/src/files/glob-source') const urlSource = require('ipfs-utils/src/files/url-source') +const Multicodecs = require('ipfs-core-utils/src/multicodecs') +const Multihashes = require('ipfs-core-utils/src/multihashes') +const Multibases = require('ipfs-core-utils/src/multibases') +const dagPb = require('@ipld/dag-pb') +const dagCbor = require('@ipld/dag-cbor') +const raw = require('multiformats/codecs/raw') +const json = require('multiformats/codecs/json') +const { sha256, sha512 } = require('multiformats/hashes/sha2') +const { identity } = require('multiformats/hashes/identity') +const { base58btc } = require('multiformats/bases/base58') /** * @typedef {import('./types').EndpointConfig} EndpointConfig * @typedef {import('./types').Options} Options + * @typedef {import('multiformats/codecs/interface').BlockCodec} BlockCodec */ /** * @param {Options} options */ function create (options = {}) { + /** + * @type {BlockCodec} + */ + const id = { + name: identity.name, + code: identity.code, + encode: (id) => id, + decode: (id) => id + } + + const bases = new Multibases({ + bases: [base58btc].concat(options.ipld && options.ipld.bases ? options.ipld.bases : []), + loadBase: options.ipld && options.ipld.loadBase + }) + const codecs = new Multicodecs({ + codecs: [dagPb, dagCbor, raw, json, id].concat(options.ipld?.codecs || []), + loadCodec: options.ipld && options.ipld.loadCodec + }) + const hashers = new Multihashes({ + hashers: [sha256, sha512, identity].concat(options.ipld && options.ipld.hashers ? options.ipld.hashers : []), + loadHasher: options.ipld && options.ipld.loadHasher + }) + /** @type {import('ipfs-core-types').IPFS & { getEndpointConfig: () => EndpointConfig }} */ const client = { add: require('./add')(options), @@ -28,7 +59,7 @@ function create (options = {}) { cat: require('./cat')(options), commands: require('./commands')(options), config: require('./config')(options), - dag: require('./dag')(options), + dag: require('./dag')(codecs, options), dht: require('./dht')(options), diag: require('./diag')(options), dns: require('./dns')(options), @@ -42,7 +73,7 @@ function create (options = {}) { ls: require('./ls')(options), mount: require('./mount')(options), name: require('./name')(options), - object: require('./object')(options), + object: require('./object')(codecs, options), pin: require('./pin')(options), ping: require('./ping')(options), pubsub: require('./pubsub')(options), @@ -53,7 +84,10 @@ function create (options = {}) { stats: require('./stats')(options), stop: require('./stop')(options), swarm: require('./swarm')(options), - version: require('./version')(options) + version: require('./version')(options), + bases, + codecs, + hashers } return client @@ -63,9 +97,6 @@ module.exports = { create, CID, multiaddr, - multibase, - multicodec, - multihash, globSource, urlSource } diff --git a/src/lib/core.js b/src/lib/core.js index e03592bd9..fc964f883 100644 --- a/src/lib/core.js +++ b/src/lib/core.js @@ -5,7 +5,7 @@ const { isBrowser, isWebWorker, isNode } = require('ipfs-utils/src/env') const { default: parseDuration } = require('parse-duration') const log = require('debug')('ipfs-http-client:lib:error-handler') const HTTP = require('ipfs-utils/src/http') -const merge = require('merge-options') +const merge = require('merge-options').bind({ ignoreUndefined: true }) const toUrlString = require('ipfs-core-utils/src/to-url-string') const http = require('http') const https = require('https') diff --git a/src/lib/ipld-formats.js b/src/lib/ipld-formats.js deleted file mode 100644 index 69d47bae0..000000000 --- a/src/lib/ipld-formats.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict' - -const dagPB = require('ipld-dag-pb') -const dagCBOR = require('ipld-dag-cbor') -const raw = require('ipld-raw') -const multicodec = require('multicodec') - -/** - * @typedef {import('cids')} CID - * @typedef {import('interface-ipld-format').Format} IPLDFormat - * @typedef {import('multicodec').CodecName} CodecName - * @typedef {import('../types').LoadFormatFn} LoadFormatFn - */ - -/** - * @type {LoadFormatFn} - */ -const noop = (codec) => { - return Promise.reject(new Error(`Missing IPLD format "${codec}"`)) -} - -/** - * Return an object containing supported IPLD Formats - * - * @param {object} [options] - IPLD options passed to the http client constructor - * @param {IPLDFormat[]} [options.formats] - A list of IPLD Formats to use - * @param {LoadFormatFn} [options.loadFormat] - An async function that can load a format when passed a codec name - */ -module.exports = ({ formats = [], loadFormat = noop } = {}) => { - formats = formats || [] - loadFormat = loadFormat || noop - - const configuredFormats = { - [multicodec.DAG_PB]: dagPB, - [multicodec.DAG_CBOR]: dagCBOR, - [multicodec.RAW]: raw - } - - formats.forEach(format => { - configuredFormats[format.codec] = format - }) - - /** - * Attempts to load an IPLD format for the passed CID - * - * @param {CodecName} codec - The code to load the format for - */ - const loadResolver = async (codec) => { - const number = multicodec.getCodeFromName(codec) - const format = configuredFormats[number] || await loadFormat(codec) - - if (!format) { - throw Object.assign( - new Error(`Missing IPLD format "${codec}"`), - { missingMulticodec: codec } - ) - } - - return format - } - - return loadResolver -} diff --git a/src/lib/parse-mtime.js b/src/lib/parse-mtime.js new file mode 100644 index 000000000..0ea352acb --- /dev/null +++ b/src/lib/parse-mtime.js @@ -0,0 +1,77 @@ +'use strict' + +const errCode = require('err-code') + +/** + * @param {any} input + */ +function parseMtime (input) { + if (input == null) { + return undefined + } + + /** @type {{ secs: number, nsecs?: number } | undefined} */ + let mtime + + // { secs, nsecs } + if (input.secs != null) { + mtime = { + secs: input.secs, + nsecs: input.nsecs + } + } + + // UnixFS TimeSpec + if (input.Seconds != null) { + mtime = { + secs: input.Seconds, + nsecs: input.FractionalNanoseconds + } + } + + // process.hrtime() + if (Array.isArray(input)) { + mtime = { + secs: input[0], + nsecs: input[1] + } + } + + // Javascript Date + if (input instanceof Date) { + const ms = input.getTime() + const secs = Math.floor(ms / 1000) + + mtime = { + secs: secs, + nsecs: (ms - (secs * 1000)) * 1000 + } + } + + /* + TODO: https://github.com/ipfs/aegir/issues/487 + + // process.hrtime.bigint() + if (input instanceof BigInt) { + const secs = input / BigInt(1e9) + const nsecs = input - (secs * BigInt(1e9)) + + mtime = { + secs: parseInt(secs.toString()), + nsecs: parseInt(nsecs.toString()) + } + } + */ + + if (!Object.prototype.hasOwnProperty.call(mtime, 'secs')) { + return undefined + } + + if (mtime != null && mtime.nsecs != null && (mtime.nsecs < 0 || mtime.nsecs > 999999999)) { + throw errCode(new Error('mtime-nsecs must be within the range [0,999999999]'), 'ERR_INVALID_MTIME_NSECS') + } + + return mtime +} + +module.exports = parseMtime diff --git a/src/lib/resolve.js b/src/lib/resolve.js new file mode 100644 index 000000000..8fb5d2294 --- /dev/null +++ b/src/lib/resolve.js @@ -0,0 +1,67 @@ +'use strict' + +const { CID } = require('multiformats/cid') +const errCode = require('err-code') + +/** + * @typedef {import('ipfs-core-types/src/utils').AbortOptions} AbortOptions + */ + +/** + * Retrieves IPLD Nodes along the `path` that is rooted at `cid`. + * + * @param {CID} cid - the CID where the resolving starts + * @param {string} path - the path that should be resolved + * @param {import('ipfs-core-utils/src/multicodecs')} codecs + * @param {(cid: CID, options?: AbortOptions) => Promise} getBlock + * @param {AbortOptions} [options] + */ +const resolve = async function * (cid, path, codecs, getBlock, options) { + /** + * @param {CID} cid + */ + const load = async (cid) => { + const codec = await codecs.getCodec(cid.code) + const block = await getBlock(cid, options) + + return codec.decode(block) + } + + const parts = path.split('/').filter(Boolean) + let value = await load(cid) + let lastCid = cid + + if (!parts.length) { + yield { + value, + remainderPath: '' + } + } + + // End iteration if there isn't a CID to follow any more + while (parts.length) { + const key = parts.shift() + + if (!key) { + throw errCode(new Error(`Could not resolve path "${path}"`), 'ERR_INVALID_PATH') + } + + if (Object.prototype.hasOwnProperty.call(value, key)) { + value = value[key] + + yield { + value, + remainderPath: parts.join('/') + } + } else { + throw errCode(new Error(`no link named "${key}" under ${lastCid}`), 'ERR_NO_LINK') + } + + if (value instanceof CID) { + lastCid = value + value = await load(value) + } + } +} + +module.exports = resolve diff --git a/src/lib/to-url-search-params.js b/src/lib/to-url-search-params.js index 1125959b8..681e0921d 100644 --- a/src/lib/to-url-search-params.js +++ b/src/lib/to-url-search-params.js @@ -1,7 +1,7 @@ 'use strict' const modeToString = require('./mode-to-string') -const { parseMtime } = require('ipfs-unixfs') +const parseMtime = require('../lib/parse-mtime') /** * @param {*} params diff --git a/src/ls.js b/src/ls.js index cec925d12..1e3fd98af 100644 --- a/src/ls.js +++ b/src/ls.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('./lib/configure') const toUrlSearchParams = require('./lib/to-url-search-params') const stat = require('./files/stat') @@ -15,7 +15,7 @@ module.exports = configure((api, opts) => { * @type {RootAPI["ls"]} */ async function * ls (path, options = {}) { - const pathStr = `${path instanceof Uint8Array ? new CID(path) : path}` + const pathStr = `${path instanceof Uint8Array ? CID.decode(path) : path}` /** * @param {*} link @@ -29,6 +29,8 @@ module.exports = configure((api, opts) => { const stats = await stat(opts)(ipfsPath) hash = stats.cid + } else { + hash = CID.parse(hash) } /** @type {import('ipfs-core-types/src/root').IPFSEntry} */ @@ -36,7 +38,7 @@ module.exports = configure((api, opts) => { name: link.Name, path: pathStr + (link.Name ? `/${link.Name}` : ''), size: link.Size, - cid: new CID(hash), + cid: hash, type: typeOf(link), depth: link.Depth || 1 } diff --git a/src/object/data.js b/src/object/data.js index 805472c3e..e651aa31b 100644 --- a/src/object/data.js +++ b/src/object/data.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -18,7 +18,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + arg: `${cid instanceof Uint8Array ? CID.decode(cid) : cid}`, ...options }), headers: options.headers diff --git a/src/object/get.js b/src/object/get.js index 0d6a8953d..562aaad26 100644 --- a/src/object/get.js +++ b/src/object/get.js @@ -1,7 +1,6 @@ 'use strict' -const CID = require('cids') -const { DAGNode, DAGLink } = require('ipld-dag-pb') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') const uint8ArrayFromString = require('uint8arrays/from-string') @@ -20,7 +19,7 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + arg: `${cid instanceof Uint8Array ? CID.decode(cid) : cid}`, dataEncoding: 'base64', ...options }), @@ -28,10 +27,14 @@ module.exports = configure(api => { }) const data = await res.json() - return new DAGNode( - uint8ArrayFromString(data.Data, 'base64pad'), - (data.Links || []).map((/** @type {any} */ l) => new DAGLink(l.Name, l.Size, l.Hash)) - ) + return { + Data: uint8ArrayFromString(data.Data, 'base64pad'), + Links: (data.Links || []).map((/** @type {any} */ link) => ({ + Name: link.Name, + Hash: CID.parse(link.Hash), + Tsize: link.Size + })) + } } return get }) diff --git a/src/object/index.js b/src/object/index.js index e0c83027e..c8c4871ef 100644 --- a/src/object/index.js +++ b/src/object/index.js @@ -1,14 +1,15 @@ 'use strict' /** + * @param {import('ipfs-core-utils/src/multicodecs')} codecs * @param {import('../types').Options} config */ -module.exports = config => ({ +module.exports = (codecs, config) => ({ data: require('./data')(config), get: require('./get')(config), links: require('./links')(config), new: require('./new')(config), patch: require('./patch')(config), - put: require('./put')(config), + put: require('./put')(codecs, config), stat: require('./stat')(config) }) diff --git a/src/object/links.js b/src/object/links.js index 2582c51c6..02e230b9a 100644 --- a/src/object/links.js +++ b/src/object/links.js @@ -1,7 +1,6 @@ 'use strict' -const CID = require('cids') -const { DAGLink } = require('ipld-dag-pb') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -19,14 +18,18 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + arg: `${cid instanceof Uint8Array ? CID.decode(cid) : cid}`, ...options }), headers: options.headers }) const data = await res.json() - return (data.Links || []).map((/** @type {any} */ l) => new DAGLink(l.Name, l.Size, l.Hash)) + return (data.Links || []).map((/** @type {any} */ l) => ({ + Name: l.Name, + Tsize: l.Size, + Hash: CID.parse(l.Hash) + })) } return links }) diff --git a/src/object/new.js b/src/object/new.js index eee675f39..718688d74 100644 --- a/src/object/new.js +++ b/src/object/new.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -26,7 +26,7 @@ module.exports = configure(api => { const { Hash } = await res.json() - return new CID(Hash) + return CID.parse(Hash) } return newObject }) diff --git a/src/object/patch/add-link.js b/src/object/patch/add-link.js index ef45de5c2..b9d20626d 100644 --- a/src/object/patch/add-link.js +++ b/src/object/patch/add-link.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../../lib/configure') const toUrlSearchParams = require('../../lib/to-url-search-params') @@ -19,7 +19,7 @@ module.exports = configure(api => { signal: options.signal, searchParams: toUrlSearchParams({ arg: [ - `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + `${cid}`, // @ts-ignore loose types dLink.Name || dLink.name || '', // @ts-ignore loose types @@ -32,7 +32,8 @@ module.exports = configure(api => { const { Hash } = await res.json() - return new CID(Hash) + return CID.parse(Hash) } + return addLink }) diff --git a/src/object/patch/append-data.js b/src/object/patch/append-data.js index ee6deb895..6eab02037 100644 --- a/src/object/patch/append-data.js +++ b/src/object/patch/append-data.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const multipartRequest = require('../../lib/multipart-request') const configure = require('../../lib/configure') const toUrlSearchParams = require('../../lib/to-url-search-params') @@ -26,7 +26,7 @@ module.exports = configure(api => { timeout: options.timeout, signal, searchParams: toUrlSearchParams({ - arg: `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + arg: `${cid}`, ...options }), ...( @@ -36,7 +36,7 @@ module.exports = configure(api => { const { Hash } = await res.json() - return new CID(Hash) + return CID.parse(Hash) } return appendData }) diff --git a/src/object/patch/rm-link.js b/src/object/patch/rm-link.js index 8881e577e..4c6faa26f 100644 --- a/src/object/patch/rm-link.js +++ b/src/object/patch/rm-link.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../../lib/configure') const toUrlSearchParams = require('../../lib/to-url-search-params') @@ -19,7 +19,7 @@ module.exports = configure(api => { signal: options.signal, searchParams: toUrlSearchParams({ arg: [ - `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + `${cid}`, // @ts-ignore loose types dLink.Name || dLink.name || null ], @@ -30,7 +30,7 @@ module.exports = configure(api => { const { Hash } = await res.json() - return new CID(Hash) + return CID.parse(Hash) } return rmLink }) diff --git a/src/object/patch/set-data.js b/src/object/patch/set-data.js index 06b4f2119..b9fa08927 100644 --- a/src/object/patch/set-data.js +++ b/src/object/patch/set-data.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const multipartRequest = require('../../lib/multipart-request') const configure = require('../../lib/configure') const toUrlSearchParams = require('../../lib/to-url-search-params') @@ -22,21 +22,23 @@ module.exports = configure(api => { const signal = abortSignal(controller.signal, options.signal) // @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90 - const { Hash } = await (await api.post('object/patch/set-data', { + const res = await api.post('object/patch/set-data', { timeout: options.timeout, signal, searchParams: toUrlSearchParams({ arg: [ - `${cid instanceof Uint8Array ? new CID(cid) : cid}` + `${cid}` ], ...options }), ...( await multipartRequest(data, controller, options.headers) ) - })).json() + }) - return new CID(Hash) + const { Hash } = await res.json() + + return CID.parse(Hash) } return setData }) diff --git a/src/object/put.js b/src/object/put.js index 81974122b..8f3297927 100644 --- a/src/object/put.js +++ b/src/object/put.js @@ -1,89 +1,33 @@ 'use strict' -const CID = require('cids') -const { DAGNode } = require('ipld-dag-pb') -const multipartRequest = require('../lib/multipart-request') const configure = require('../lib/configure') -const toUrlSearchParams = require('../lib/to-url-search-params') -const abortSignal = require('../lib/abort-signal') -const { AbortController } = require('native-abort-controller') -const uint8ArrayToString = require('uint8arrays/to-string') -const uint8ArrayFromString = require('uint8arrays/from-string') /** * @typedef {import('../types').HTTPClientExtraOptions} HTTPClientExtraOptions * @typedef {import('ipfs-core-types/src/object').API} ObjectAPI */ -module.exports = configure(api => { - /** - * @type {ObjectAPI["put"]} - */ - async function put (obj, options = {}) { - let tmpObj = { - /** @type {string | undefined} */ - Data: undefined, - /** @type {{ Name: string, Hash: string, Size: number }[]} */ - Links: [] - } - - if (obj instanceof Uint8Array) { - if (!options.enc) { - tmpObj = { - // FIXME: this will corrupt data for byte values over 127 - Data: uint8ArrayToString(obj), - Links: [] - } - } - } else if (obj instanceof DAGNode) { - tmpObj = { - // FIXME: this will corrupt data for byte values over 127 - Data: uint8ArrayToString(obj.Data), - Links: obj.Links.map(l => ({ - Name: l.Name, - Hash: l.Hash.toString(), - Size: l.Tsize - })) - } - } else if (typeof obj === 'object') { - // FIXME: this will corrupt data for for byte values over 127 - if (obj.Data) { - tmpObj.Data = uint8ArrayToString(obj.Data) - } - - if (obj.Links) { - // @ts-ignore Size is Tsize - tmpObj.Links = obj.Links - } - } else { - throw new Error('obj not recognized') - } - - let buf - if (obj instanceof Uint8Array && options.enc) { - buf = obj - } else { - options.enc = 'json' - buf = uint8ArrayFromString(JSON.stringify(tmpObj)) +/** + * @param {import('ipfs-core-utils/src/multicodecs')} codecs + * @param {import('../types').Options} options + */ +module.exports = (codecs, options) => { + const fn = configure((api) => { + const dagPut = require('../dag/put')(codecs, options) + + /** + * @type {ObjectAPI["put"]} + */ + async function put (obj, options = {}) { + return dagPut(obj, { + ...options, + format: 'dag-pb', + hashAlg: 'sha2-256', + version: 0 + }) } + return put + }) - // allow aborting requests on body errors - const controller = new AbortController() - const signal = abortSignal(controller.signal, options.signal) - - // @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90 - const res = await api.post('object/put', { - timeout: options.timeout, - signal, - searchParams: toUrlSearchParams(options), - ...( - await multipartRequest(buf, controller, options.headers) - ) - }) - - const { Hash } = await res.json() - - return new CID(Hash) - } - return put -}) + return fn(options) +} diff --git a/src/object/stat.js b/src/object/stat.js index 70307ba59..4d7077a1f 100644 --- a/src/object/stat.js +++ b/src/object/stat.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -18,13 +18,18 @@ module.exports = configure(api => { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: `${cid instanceof Uint8Array ? new CID(cid) : cid}`, + arg: `${cid}`, ...options }), headers: options.headers }) - return res.json() + const output = await res.json() + + return { + ...output, + Hash: CID.parse(output.Hash) + } } return stat }) diff --git a/src/pin/add-all.js b/src/pin/add-all.js index fbfa2317b..bf0a4e290 100644 --- a/src/pin/add-all.js +++ b/src/pin/add-all.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const normaliseInput = require('ipfs-core-utils/src/pins/normalise-input') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -32,12 +32,12 @@ module.exports = configure(api => { for await (const pin of res.ndjson()) { if (pin.Pins) { // non-streaming response for (const cid of pin.Pins) { - yield new CID(cid) + yield CID.parse(cid) } continue } - yield new CID(pin) + yield CID.parse(pin) } } } diff --git a/src/pin/ls.js b/src/pin/ls.js index 07850063c..859f1499d 100644 --- a/src/pin/ls.js +++ b/src/pin/ls.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -18,7 +18,7 @@ function toPin (type, cid, metadata) { /** @type {import('ipfs-core-types/src/pin').LsResult} */ const pin = { type, - cid: new CID(cid) + cid: CID.parse(cid) } if (metadata) { diff --git a/src/pin/remote/index.js b/src/pin/remote/index.js index aa8ffd66d..29db83484 100644 --- a/src/pin/remote/index.js +++ b/src/pin/remote/index.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const Client = require('../../lib/core') const Service = require('./service') const toUrlSearchParams = require('../../lib/to-url-search-params') @@ -96,7 +96,7 @@ Remote.prototype.rmAll = async function ({ timeout, signal, headers, ...query }) */ const decodePin = ({ Name: name, Status: status, Cid: cid }) => { return { - cid: new CID(cid), + cid: CID.parse(cid), name, status } @@ -119,10 +119,10 @@ const encodeService = (service) => { * @returns {string} */ const encodeCID = (cid) => { - if (CID.isCID(cid)) { + if (cid instanceof CID) { return cid.toString() } else { - throw new TypeError(`CID instance expected instead of ${cid}`) + throw new TypeError(`CID instance expected instead of ${typeof cid}`) } } diff --git a/src/pin/remote/service.js b/src/pin/remote/service.js index f5cc65861..a6892955e 100644 --- a/src/pin/remote/service.js +++ b/src/pin/remote/service.js @@ -11,6 +11,7 @@ const toUrlSearchParams = require('../../lib/to-url-search-params') * @typedef {import('ipfs-core-types/src/pin/remote/service').RemotePinServiceWithStat} RemotePinServiceWithStat * @typedef {import('../../types').HTTPClientExtraOptions} HTTPClientExtraOptions * @typedef {import('ipfs-core-types/src/pin/remote/service').API} RemotePiningServiceAPI + * @typedef {import('ipfs-core-types/src/pin/remote/service').Stat} Stat */ class Service { /** @@ -120,8 +121,7 @@ Service.prototype.ls = async function ls (options = {}) { /** @type {{RemoteServices: Object[]}} */ const { RemoteServices } = await response.json() - /** @type {Stat extends true ? RemotePinServiceWithStat[] : RemotePinService []} */ - return (RemoteServices.map(Service.decodeRemoteService)) + return RemoteServices.map(Service.decodeRemoteService) } module.exports = Service diff --git a/src/pin/rm-all.js b/src/pin/rm-all.js index d4d0ca65b..98a61dc31 100644 --- a/src/pin/rm-all.js +++ b/src/pin/rm-all.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const normaliseInput = require('ipfs-core-utils/src/pins/normalise-input') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -34,10 +34,10 @@ module.exports = configure(api => { for await (const pin of res.ndjson()) { if (pin.Pins) { // non-streaming response - yield * pin.Pins.map((/** @type {string} */ cid) => new CID(cid)) + yield * pin.Pins.map((/** @type {string} */ cid) => CID.parse(cid)) continue } - yield new CID(pin) + yield CID.parse(pin) } } } diff --git a/src/refs/index.js b/src/refs/index.js index cdab0085b..6f610d7fd 100644 --- a/src/refs/index.js +++ b/src/refs/index.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const toCamel = require('../lib/object-to-camel') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -15,15 +15,14 @@ module.exports = configure((api, opts) => { * @type {RefsAPI["refs"]} */ const refs = async function * (args, options = {}) { - if (!Array.isArray(args)) { - args = [args] - } + /** @type {import('ipfs-core-types/src/utils').IPFSPath[]} */ + const argsArr = Array.isArray(args) ? args : [args] const res = await api.post('refs', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams({ - arg: args.map(arg => `${arg instanceof Uint8Array ? new CID(arg) : arg}`), + arg: argsArr.map(arg => `${arg instanceof Uint8Array ? CID.decode(arg) : arg}`), ...options }), headers: options.headers, diff --git a/src/repo/gc.js b/src/repo/gc.js index a1199f21f..49f480506 100644 --- a/src/repo/gc.js +++ b/src/repo/gc.js @@ -1,6 +1,6 @@ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const configure = require('../lib/configure') const toUrlSearchParams = require('../lib/to-url-search-params') @@ -22,7 +22,7 @@ module.exports = configure(api => { transform: (res) => { return { err: res.Error ? new Error(res.Error) : null, - cid: (res.Key || {})['/'] ? new CID(res.Key['/']) : null + cid: (res.Key || {})['/'] ? CID.parse(res.Key['/']) : null } } }) diff --git a/src/types.d.ts b/src/types.d.ts index 0579c4952..f2403a6f7 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,10 +1,7 @@ -import { Format as IPLDFormat } from 'interface-ipld-format' import { Agent as HttpAgent } from 'http' import { Agent as HttpsAgent } from 'https' import { Multiaddr } from 'multiaddr' -import { CodecName } from 'multicodec' - -export type LoadFormatFn = (name: CodecName) => Promise> +import type { BlockCodec } from 'multiformats/codecs/interface' export interface Options { host?: string @@ -18,9 +15,17 @@ export interface Options { agent?: HttpAgent | HttpsAgent } +export interface LoadBaseFn { (codeOrName: number | string): Promise> } +export interface LoadCodecFn { (codeOrName: number | string): Promise> } +export interface LoadHasherFn { (codeOrName: number | string): Promise } + export interface IPLDOptions { - formats?: IPLDFormat[] - loadFormat?: LoadFormatFn + loadBase: LoadBaseFn + loadCodec: LoadCodecFn + loadHasher: LoadHasherFn + bases: Array> + codecs: Array> + hashers: MultihashHasher[] } export interface HTTPClientExtraOptions { @@ -29,9 +34,9 @@ export interface HTTPClientExtraOptions { } export interface EndpointConfig { - host: string, - port: string, - protocol: string, + host: string + port: string + protocol: string pathname: string 'api-path': string } diff --git a/test/dag.spec.js b/test/dag.spec.js index 34cbaf497..40ba0fcee 100644 --- a/test/dag.spec.js +++ b/test/dag.spec.js @@ -5,9 +5,11 @@ const uint8ArrayFromString = require('uint8arrays/from-string') const { expect } = require('aegir/utils/chai') -const ipldDagPb = require('ipld-dag-pb') -const { DAGNode } = ipldDagPb -const CID = require('cids') +const dagPb = require('@ipld/dag-pb') +const dagCbor = require('@ipld/dag-cbor') +const raw = require('multiformats/codecs/raw') +const { base58btc } = require('multiformats/bases/base58') +const { base32 } = require('multiformats/bases/base32') const f = require('./utils/factory')() const ipfsHttpClient = require('../src') @@ -23,13 +25,14 @@ describe('.dag', function () { it('should be able to put and get a DAG node with format dag-pb', async () => { const data = uint8ArrayFromString('some data') - const node = new DAGNode(data) + const node = { + Data: data, + Links: [] + } - let cid = await ipfs.dag.put(node, { format: 'dag-pb', hashAlg: 'sha2-256' }) - cid = cid.toV0() - expect(cid.codec).to.equal('dag-pb') - cid = cid.toBaseEncodedString('base58btc') - expect(cid).to.equal('Qmd7xRhW5f29QuBFtqu3oSD27iVy35NRB91XFjmKFhtgMr') + const cid = await ipfs.dag.put(node, { format: 'dag-pb', hashAlg: 'sha2-256', cidVersion: 0 }) + expect(cid.code).to.equal(dagPb.code) + expect(cid.toString(base58btc)).to.equal('Qmd7xRhW5f29QuBFtqu3oSD27iVy35NRB91XFjmKFhtgMr') const result = await ipfs.dag.get(cid) @@ -38,11 +41,10 @@ describe('.dag', function () { it('should be able to put and get a DAG node with format dag-cbor', async () => { const cbor = { foo: 'dag-cbor-bar' } - let cid = await ipfs.dag.put(cbor, { format: 'dag-cbor', hashAlg: 'sha2-256' }) + const cid = await ipfs.dag.put(cbor, { format: 'dag-cbor', hashAlg: 'sha2-256' }) - expect(cid.codec).to.equal('dag-cbor') - cid = cid.toBaseEncodedString('base32') - expect(cid).to.equal('bafyreic6f672hnponukaacmk2mmt7vs324zkagvu4hcww6yba6kby25zce') + expect(cid.code).to.equal(dagCbor.code) + expect(cid.toString(base32)).to.equal('bafyreic6f672hnponukaacmk2mmt7vs324zkagvu4hcww6yba6kby25zce') const result = await ipfs.dag.get(cid) @@ -51,11 +53,10 @@ describe('.dag', function () { it('should be able to put and get a DAG node with format raw', async () => { const node = uint8ArrayFromString('some data') - let cid = await ipfs.dag.put(node, { format: 'raw', hashAlg: 'sha2-256' }) + const cid = await ipfs.dag.put(node, { format: 'raw', hashAlg: 'sha2-256' }) - expect(cid.codec).to.equal('raw') - cid = cid.toBaseEncodedString('base32') - expect(cid).to.equal('bafkreiata6mq425fzikf5m26temcvg7mizjrxrkn35swuybmpah2ajan5y') + expect(cid.code).to.equal(raw.code) + expect(cid.toString(base32)).to.equal('bafkreiata6mq425fzikf5m26temcvg7mizjrxrkn35swuybmpah2ajan5y') const result = await ipfs.dag.get(cid) @@ -63,17 +64,17 @@ describe('.dag', function () { }) it('should error when missing DAG resolver for multicodec from requested CID', async () => { - const block = await ipfs.block.put(Uint8Array.from([0, 1, 2, 3]), { - cid: new CID('z8mWaJ1dZ9fH5EetPuRsj8jj26pXsgpsr') + const cid = await ipfs.block.put(Uint8Array.from([0, 1, 2, 3]), { + format: 'git-raw' }) - await expect(ipfs.dag.get(block.cid)).to.eventually.be.rejectedWith('Missing IPLD format "git-raw"') + await expect(ipfs.dag.get(cid)).to.eventually.be.rejectedWith(/No codec found/) }) it('should error when putting node with esoteric format', () => { const node = uint8ArrayFromString('some data') - return expect(ipfs.dag.put(node, { format: 'git-raw', hashAlg: 'sha2-256' })).to.eventually.be.rejectedWith(/Missing IPLD format/) + return expect(ipfs.dag.put(node, { format: 'git-raw', hashAlg: 'sha2-256' })).to.eventually.be.rejectedWith(/No codec found/) }) it('should attempt to load an unsupported format', async () => { @@ -81,12 +82,10 @@ describe('.dag', function () { const ipfs2 = ipfsHttpClient.create({ url: `http://${ipfs.apiHost}:${ipfs.apiPort}`, ipld: { - loadFormat: (format) => { + loadCodec: (format) => { askedToLoadFormat = format === 'git-raw' return { - util: { - serialize: (buf) => buf - } + encode: (buf) => buf } } } @@ -104,9 +103,12 @@ describe('.dag', function () { const ipfs2 = ipfsHttpClient.create({ url: `http://${ipfs.apiHost}:${ipfs.apiPort}`, ipld: { - formats: [ - ipldDagPb - ] + codecs: [{ + name: 'custom-codec', + code: 1337, + encode: (thing) => thing, + decode: (thing) => thing + }] } }) @@ -118,7 +120,10 @@ describe('.dag', function () { hashAlg: 'sha2-256' }) - const dagPbNode = new DAGNode(new Uint8Array(0), [], 0) + const dagPbNode = { + Data: new Uint8Array(0), + Links: [] + } const cid2 = await ipfs2.dag.put(dagPbNode, { format: 'dag-pb', hashAlg: 'sha2-256' diff --git a/test/exports.spec.js b/test/exports.spec.js index b13008847..104133d9a 100644 --- a/test/exports.spec.js +++ b/test/exports.spec.js @@ -1,11 +1,8 @@ /* eslint-env mocha, browser */ 'use strict' -const CID = require('cids') +const { CID } = require('multiformats/cid') const { multiaddr } = require('multiaddr') -const multibase = require('multibase') -const multicodec = require('multicodec') -const multihash = require('multihashes') const { expect } = require('aegir/utils/chai') const IpfsHttpClient = require('../') @@ -14,8 +11,5 @@ describe('exports', () => { it('should export the expected types and utilities', () => { expect(IpfsHttpClient.CID).to.equal(CID) expect(IpfsHttpClient.multiaddr).to.equal(multiaddr) - expect(IpfsHttpClient.multibase).to.equal(multibase) - expect(IpfsHttpClient.multicodec).to.equal(multicodec) - expect(IpfsHttpClient.multihash).to.equal(multihash) }) }) diff --git a/test/files.spec.js b/test/files.spec.js index 10e2d1c84..2f4056f30 100644 --- a/test/files.spec.js +++ b/test/files.spec.js @@ -5,6 +5,7 @@ const uint8ArrayFromString = require('uint8arrays/from-string') const { expect } = require('aegir/utils/chai') const f = require('./utils/factory')() +const dagPb = require('@ipld/dag-pb') describe('.add', function () { this.timeout(20 * 1000) @@ -32,7 +33,7 @@ describe('.add', function () { expect(result).to.have.property('cid') const { cid } = result - expect(cid).to.have.property('codec', 'dag-pb') + expect(cid).to.have.property('code', dagPb.code) expect(cid.toString()).to.equal('QmVv4Wz46JaZJeH5PMV4LGbRiiMKEmszPYY3g6fjGnVXBS') }) }) diff --git a/test/utils/factory.js b/test/utils/factory.js index 3a0bd3961..a3d87237b 100644 --- a/test/utils/factory.js +++ b/test/utils/factory.js @@ -2,7 +2,7 @@ // @ts-ignore no types const { createFactory } = require('ipfsd-ctl') -const merge = require('merge-options') +const merge = require('merge-options').bind({ ignoreUndefined: true }) const { isNode } = require('ipfs-utils/src/env') const commonOptions = {