Skip to content
This repository has been archived by the owner on Jul 3, 2019. It is now read-only.

feat(transports) add support for IPFS and IPNS #173

Open
wants to merge 2 commits into
base: latest
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ function getFetcher (type) {
case 'version':
fetchers[type] = require('./fetchers/version')
break
case 'ipfs':
fetchers[type] = require('./fetchers/ipfs')
break
case 'ipns':
fetchers[type] = require('./fetchers/ipns')
break
default:
throw new Error(`Invalid dependency type requested: ${type}`)
}
Expand Down
37 changes: 37 additions & 0 deletions lib/fetchers/ipfs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict'

const BB = require('bluebird')
const Fetcher = require('../fetch')
const getIpfs = require('../util/get-ipfs')
const CID = require('ipfs-http-client').CID
const PassThrough = require('stream').PassThrough

const fetchIPFS = module.exports = Object.create(null)

Fetcher.impl(fetchIPFS, {
packument () {
return BB.reject(new Error('Not implemented yet'))
},

manifest () {
return BB.resolve(null)
},

tarball (spec, opts) {
try {
const ipfs = getIpfs(opts)
const cid = new CID(spec.rawSpec.trim().replace('ipfs://', ''))

return ipfs.catReadableStream(cid)
} catch (err) {
const stream = new PassThrough()
stream.emit('error', err)

return stream
}
},

fromManifest (manifest, spec, opts) {
return this.tarball(manifest || spec, opts)
}
})
108 changes: 108 additions & 0 deletions lib/fetchers/ipns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
'use strict'

const BB = require('bluebird')
const Fetcher = require('../fetch')
const PassThrough = require('stream').PassThrough
const pickManifest = require('npm-pick-manifest')
const pipe = BB.promisify(require('mississippi').pipe)
const optCheck = require('../util/opt-check')
const LRU = require('lru-cache')
const getIpfs = require('../util/get-ipfs')

const MEMO = new LRU({
length: m => m._contentLength,
max: 200 * 1024 * 1024, // 200MB
maxAge: 30 * 1000 // 30s
})

const resolveIpnsName = (name, ipfs, opts) => {
if (MEMO.has(name)) {
return BB.resolve(MEMO.get(name))
}

opts.log.info('ipns', `Resolving ${name}`)

let start = Date.now()

return ipfs.name.resolve(name)
.then(cid => {
opts.log.info('ipns', `Resolved ${name} to ${cid}`, Date.now() - start, 'ms')

MEMO.set(name, cid)

return cid
})
}

const fetchIPNS = module.exports = Object.create(null)

Fetcher.impl(fetchIPNS, {
clearMemoized () {
MEMO.reset()
},

packument (spec, opts) {
opts = optCheck(opts)

const ipfs = getIpfs(opts)
const result = spec.rawSpec.match(/.*:\/\/([a-zA-Z0-9]*)#?(.*)/)
const name = result[1]

return resolveIpnsName(name, ipfs, opts)
.then(cid => ipfs.cat(cid))
.then(buf => JSON.parse(buf.toString()))
},

manifest (spec, opts) {
opts = optCheck(opts)

const result = spec.rawSpec.match(/.*:\/\/([a-zA-Z0-9]*)#?(.*)/)
let version = result[2]

return this.packument(spec, opts)
.then(packument => {
if (!version) {
if (packument['dist-tags'] && packument['dist-tags'].latest) {
version = packument['dist-tags'].latest
} else {
version = '*'
}
}

return pickManifest(packument, version, {
defaultTag: opts.defaultTag,
enjoyBy: opts.enjoyBy,
includeDeprecated: opts.includeDeprecated
})
})
},

tarball (spec, opts) {
const stream = new PassThrough()

this.manifest(spec, opts)
.then(manifest => {
return pipe(this.fromManifest(
manifest, spec, opts
), stream)
})
.catch(err => stream.emit('error', err))

return stream
},

fromManifest (manifest, spec, opts) {
opts = optCheck(opts)

const ipfs = getIpfs(opts)

if (!manifest.dist.cid) {
const err = new Error(`No CID found for ${manifest.name}@${manifest.version}`)
err.code = 'ENOCID'

throw err
}

return ipfs.catReadableStream(`/ipfs/${manifest.dist.cid}`)
}
})
22 changes: 22 additions & 0 deletions lib/util/get-ipfs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const ipfsClient = require('ipfs-http-client')

let node

const getIpfs = (opts) => {
if (!opts['ipfs-url']) {
const err = new Error('Please specify an IPFS daemon to connect to, e.g. --ipfs-url=/ip4/127.0.0.1/tcp/5001 or npm config set ipfs-url /ip4/127.0.0.1/tcp/5001')
err.code = 'ENOIPFSURL'

throw err
}

if (!node) {
node = ipfsClient(opts['ipfs-url'])
}

return node
}

module.exports = getIpfs
1 change: 1 addition & 0 deletions lib/util/opt-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = figgyPudding({
'full-metadata': { default: false },
gid: {},
git: {},
'ipfs-url': {},
includeDeprecated: { default: true },
'include-deprecated': 'includeDeprecated',
integrity: {},
Expand Down
Loading