return promisify(async (name, opts, cb) => { if (typeof opts === 'function') { cb = opts opts = {} } opts = opts || {} if (!isIpfs.path(name)) { return setImmediate(() => cb(new Error('invalid argument ' + name))) } // TODO remove this and update subsequent code when IPNS is implemented if (!isIpfs.ipfsPath(name)) { return setImmediate(() => cb(new Error('resolve non-IPFS names is not implemented'))) } const split = name.split('/') // ['', 'ipfs', 'hash', ...path] const cid = new CID(split[2]) if (split.length === 3) { return setImmediate(() => cb(null, `/ipfs/${cidToString(cid, { base: opts.cidBase })}`)) } const path = split.slice(3).join('/') const results = self._ipld.resolve(cid, path) let value = cid let remainderPath = path try { for await (const result of results) { if (result.remainderPath === '') { // Use values from previous iteration if the value isn't a CID if (CID.isCID(result.value)) { value = result.value remainderPath = '' } if (result.value && CID.isCID(result.value.Hash)) { value = result.value.Hash remainderPath = '' } break } value = result.value remainderPath = result.remainderPath } } catch (error) { return cb(error) } return cb(null, `/ipfs/${cidToString(value, { base: opts.cidBase })}${remainderPath ? '/' + remainderPath : ''}`) })
test('should add and take last', async t => { const { node } = t.context const data = randomBytes(randomInteger(1, 256)) const { cid, path } = await node.add(data).last() t.true(CID.isCID(cid)) t.true(isString(path)) })
return promisify((args, opts, callback) => { // TODO this needs to be adjusted with the new go-ipfs http-api if (args && CID.isCID(args)) { args = multihash.toB58String(args.multihash) } if (typeof (opts) === 'function') { callback = opts opts = {} } const request = { path: 'block/stat', args: args, qs: opts } // Transform the response from { Key, Size } objects to { key, size } objects const transform = (stats, callback) => { callback(null, { key: stats.Key, size: stats.Size }) } send.andTransform(request, transform, callback) })
const generator = async function * () { // End iteration if there isn't a CID to follow anymore while (cid !== null) { const format = await this._getFormat(cid.codec) // get block // use local resolver // update path value const block = await promisify(this.bs.get.bind(this.bs))(cid) const result = await promisify(format.resolver.resolve)(block.data, path) // Prepare for the next iteration if there is a `remainderPath` path = result.remainderPath let value = result.value // NOTE vmx 2018-11-29: Not all IPLD Formats return links as // CIDs yet. Hence try to convert old style links to CIDs if (Object.keys(value).length === 1 && '/' in value) { try { value = new CID(value['/']) } catch (_error) { value = null } } cid = CID.isCID(value) ? value : null yield { remainderPath: path, value } } }.bind(this)
hasDescendant: (root, childhash, callback) => { const seen = {} if (CID.isCID(childhash) || Buffer.isBuffer(childhash)) { childhash = toB58String(childhash) } return searchChildren(root, callback) function searchChildren (root, cb) { someSeries(root.Links, (link, done) => { const cid = link.Hash const bs58Link = toB58String(cid) if (bs58Link === childhash) { return done(null, true) } if (bs58Link in seen) { return done(null, false) } seen[bs58Link] = true dag.get(cid, '', { preload: false }, (err, res) => { if (err) { return done(err) } searchChildren(res.value, done) }) }, cb) } },
test('should add from iterable of string', async t => { const { node } = t.context const data = randomArray(1, 100, () => randomBytes(randomInteger(1, 64)).toString('hex')) for await (const { cid, path } of node.add(data)) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
test('should add from object of string', async t => { const { node } = t.context const data = { content: randomBytes(randomInteger(1, 64)).toString('hex') } for await (const { cid, path } of node.add(data)) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
test('should add from Node.js stream', async t => { const { node } = t.context const data = Fs.createReadStream(__filename) for await (const { cid, path } of node.add(data)) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
test('should add from buffer', async t => { const { node } = t.context const data = randomBytes(randomInteger(1, 256)) for await (const { cid, path } of node.add(data)) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
test('should add from iterable of object of iterable', async t => { const { node } = t.context const data = randomArray(2, 100, () => randomBytes(randomInteger(1, 64))) .map(chunk => ({ content: [chunk] })) for await (const { cid, path } of node.add(data)) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
/** * Return a CID instance if it is a link. * * If something is a link `{"/": "baseencodedcid"}` or a CID, then return * a CID object, else return `null`. * * @param {*} link - The object to check * @returns {?CID} - A CID instance */ static _maybeCID (link) { if (CID.isCID(link)) { return link } if (link && link['/'] !== undefined) { return new CID(link['/']) } return null }
test('should add from iterator of buffer', async t => { const { node } = t.context const data = randomArray(1, 100, () => randomBytes(randomInteger(1, 64))) const iterator = function * () { for (let i = 0; i < data.length; i++) yield data[i] } for await (const { cid, path } of node.add(iterator())) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
return promisify((args, opts, callback) => { if (typeof opts === 'function') { callback = opts opts = {} } // TODO this needs to be adjusted with the new go-ipfs http-api let cid try { if (CID.isCID(args)) { cid = args args = cid.toBaseEncodedString() } else if (Buffer.isBuffer(args)) { cid = new CID(args) args = cid.toBaseEncodedString() } else if (typeof args === 'string') { cid = new CID(args) } else { return callback(new Error('invalid argument')) } } catch (err) { return callback(err) } // Transform the response from Buffer or a Stream to a Block const transform = (res, callback) => { if (Buffer.isBuffer(res)) { callback(null, new Block(res, cid)) // For empty blocks, concat-stream can't infer the encoding so we are // passed back an empty array } else if (Array.isArray(res) && res.length === 0) { callback(null, new Block(Buffer.alloc(0), cid)) } else { streamToValue(res, (err, data) => { if (err) { return callback(err) } // For empty blocks, concat-stream can't infer the encoding so we are // passed back an empty array if (!data.length) data = Buffer.alloc(0) callback(null, new Block(data, cid)) }) } } const request = { path: 'block/get', args: args, qs: opts } send.andTransform(request, transform, callback) })
test('should add from object of iterator', async t => { const { node } = t.context const data = { content: (function * () { yield randomBytes(randomInteger(1, 64)) })() } for await (const { cid, path } of node.add(data)) { t.true(CID.isCID(cid)) t.true(isString(path)) } })
module.exports = function (cid) { if (Buffer.isBuffer(cid)) { return new CID(cid).toString() } if (CID.isCID(cid)) { return cid.toString() } if (typeof cid !== 'string') { throw new Error('unexpected cid type: ' + typeof cid) } new CID(cid.split('/')[0]) // eslint-disable-line no-new return cid }
test('should stat a directory', async t => { const { node } = t.context const path = `/test/test-stat-${shortid()}.txt` const data = randomBytes(256) await node.write(path, data, { parents: true }) const stats = await node.stat(Path.dirname(path)) t.true(CID.isCID(stats.cid)) t.is(stats.size, 0) t.true(stats.cumulativeSize > data.length) t.is(stats.blocks, 1) t.is(stats.type, 'directory') })
test('should stat a file', async t => { const { node } = t.context const path = `/test-stat-${shortid()}.txt` const data = randomBytes(256) await node.write(path, data) const stats = await node.stat(path) t.true(CID.isCID(stats.cid)) t.is(stats.size, data.length) t.true(stats.cumulativeSize > data.length) t.is(stats.blocks, 1) t.is(stats.type, 'file') })
res.forEach(({ cid, path }) => { t.true(CID.isCID(cid)) t.true(isString(path)) })