diff --git a/API.md b/API.md
new file mode 100644
index 000000000..84044df8f
--- /dev/null
+++ b/API.md
@@ -0,0 +1,695 @@
+# TOC
+ - [.add](#add)
+ - [.block](#block)
+ - [.cat](#cat)
+ - [.commands](#commands)
+ - [.config](#config)
+ - [.dht](#dht)
+ - [.diag](#diag)
+ - [.id](#id)
+ - [API](#api)
+ - [.log](#log)
+ - [.mount](#mount)
+ - [.name](#name)
+ - [.object](#object)
+ - [.pin](#pin)
+ - [.ping](#ping)
+ - [.refs](#refs)
+ - [.send](#send)
+ - [.swarm](#swarm)
+ - [.update (currently disabled, wait for IPFS 0.4.0)](#update-currently-disabled-wait-for-ipfs-040)
+ - [.version](#version)
+
+
+# .add
+add file.
+
+```js
+done => {
+ if (!isNode) {
+ return done()
+ }
+ const file = new File({
+ cwd: path.dirname(testfilePath),
+ base: path.dirname(testfilePath),
+ path: testfilePath,
+ contents: new Buffer(testfile)
+ })
+ apiClients['a'].add
+ if (err) throw err
+ const added = res[0] != null ? res[0] : res
+ assert.equal(added.Hash, 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
+ assert.equal(added.Name, path.basename(testfilePath))
+ done()
+ })
+```
+
+add buffer.
+
+```js
+done => {
+ let buf = new Buffer(testfile)
+ apiClients['a'].add
+ if (err) throw err
+ assert.equal(res.length, 1)
+ const added = res[0]
+ assert.equal(added.Hash, 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
+ done()
+ })
+```
+
+add BIG buffer.
+
+```js
+done => {
+ if (!isNode) {
+ return done()
+ }
+ apiClients['a'].add
+ if (err) throw err
+ assert.equal(res.length, 1)
+ const added = res[0]
+ assert.equal(added.Hash, 'Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq')
+ done()
+ })
+```
+
+add path.
+
+```js
+done => {
+ if (!isNode) {
+ return done()
+ }
+ apiClients['a'].add
+ if (err) throw err
+ const added = res[0] != null ? res[0] : res
+ assert.equal(added.Hash, 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
+ done()
+ })
+```
+
+add a nested dir.
+
+```js
+done => {
+ apiClients['a'].add
+ if (isNode) {
+ if (err) throw err
+ const added = res[res.length - 1]
+ assert.equal(added.Hash, 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg')
+ done()
+ } else {
+ assert.equal(err.message, 'Recursive uploads are not supported in the browser')
+ done()
+ }
+ })
+```
+
+add stream.
+
+```js
+done => {
+ const stream = new Readable()
+ stream.push('Hello world')
+ stream.push(null)
+ apiClients['a'].add
+ if (err) throw err
+ const added = res[0] != null ? res[0] : res
+ assert.equal(added.Hash, 'QmNRCQWfgze6AbBCaT1rkrkV5tJ2aP4oTNPb5JZcXYywve')
+ done()
+ })
+```
+
+add url.
+
+```js
+done => {
+ const url = 'https://raw.githubusercontent.com/ipfs/js-ipfs-api/2a9cc63d7427353f2145af6b1a768a69e67c0588/README.md'
+ apiClients['a'].add
+ if (err) throw err
+ const added = res[0] != null ? res[0] : res
+ assert.equal(added.Hash, 'QmZmHgEX9baxUn3qMjsEXQzG6DyNcrVnwieQQTrpDdrFvt')
+ done()
+ })
+```
+
+
+# .block
+block.put.
+
+```js
+done => {
+ apiClients['a'].block.put
+ if (err) throw err
+ const store = res.Key
+ assert.equal(store, 'QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ')
+ done()
+ })
+```
+
+block.get.
+
+```js
+done => {
+ apiClients['a'].block.get
+ if (err) throw err
+ let buf = ''
+ res
+ .on('data', function (data) { buf += data })
+ .on('end', function () {
+ assert.equal(buf, 'blorb')
+ done()
+ })
+ })
+```
+
+
+# .cat
+cat.
+
+```js
+done => {
+ apiClients['a'].cat
+ if (err) {
+ throw err
+ }
+ let buf = ''
+ res
+ .on('error', err => { throw err })
+ .on('data', data => buf += data)
+ .on('end', () => {
+ assert.equal(buf, testfile)
+ done()
+ })
+ })
+```
+
+cat BIG file.
+
+```js
+done => {
+ if (!isNode) {
+ return done()
+ }
+ apiClients['a'].cat
+ if (err) {
+ throw err
+ }
+ testfileBig = require('fs').createReadStream(__dirname + '/../15mb.random', { bufferSize: 128 })
+ // Do not blow out the memory of nodejs :)
+ streamEqual(res, testfileBig, (err, equal) => {
+ if (err) throw err
+ assert(equal)
+ done()
+ })
+ })
+```
+
+
+# .commands
+lists commands.
+
+```js
+done => {
+ apiClients['a'].commands
+ if (err) {
+ throw err
+ }
+ assert(res)
+ done()
+ })
+```
+
+
+# .config
+.config.{set, get}.
+
+```js
+done => {
+ const confKey = 'arbitraryKey'
+ const confVal = 'arbitraryVal'
+ apiClients['a'].config.set
+ if (err) throw err
+ apiClients['a'].config.get(confKey, (err, res) => {
+ if (err) throw err
+ assert.equal(res.Value, confVal)
+ done()
+ })
+ })
+```
+
+.config.show.
+
+```js
+done => {
+ apiClients['c'].config.show
+ if (err) {
+ throw err
+ }
+ assert(res)
+ done()
+ })
+```
+
+.config.replace.
+
+```js
+done => {
+ if (!isNode) {
+ return done()
+ }
+ apiClients['c'].config.replace
+ if (err) {
+ throw err
+ }
+ assert.equal(res, null)
+ done()
+ })
+```
+
+
+# .dht
+returns an error when getting a non-existent key from the DHT.
+
+```js
+done => {
+ apiClients['a'].dht.get
+ assert(err)
+ done()
+ })
+```
+
+puts and gets a key value pair in the DHT.
+
+```js
+done => {
+ apiClients['a'].dht.put
+ if (err) {
+ throw err
+ }
+ assert.equal(typeof res, 'object')
+ return done()
+ // non ipns or pk hashes fail to fetch, known bug
+ // bug: https://github.com/ipfs/go-ipfs/issues/1923#issuecomment-152932234
+ // apiClients['a'].dht.get('scope', (err, value) => {
+ // console.log('->>', err, value)
+ // if (err) {
+ // throw err
+ // }
+ // assert.equal(value, 'interplanetary')
+ // done()
+ // })
+ })
+```
+
+.dht.findprovs.
+
+```js
+done => {
+ apiClients['a'].dht.findprovs
+ if (err) {
+ throw err
+ }
+ assert.equal(typeof res, 'object')
+ assert(res)
+ done()
+ })
+```
+
+
+# .diag
+.diag.net.
+
+```js
+done => {
+ apiClients['a'].diag.net
+ if (err) {
+ throw err
+ }
+ assert(res)
+ done()
+ })
+```
+
+.diag.sys.
+
+```js
+done => {
+ apiClients['a'].diag.sys
+ if (err) {
+ throw err
+ }
+ assert(res)
+ assert(res.memory)
+ assert(res.diskinfo)
+ done()
+ })
+```
+
+
+# .id
+id.
+
+```js
+done => {
+ apiClients['a'].id
+ if (err) throw err
+ const id = res
+ assert(id.ID)
+ assert(id.PublicKey)
+ done()
+ })
+```
+
+
+# API
+has the api object.
+
+```js
+assert(apiClients['a'])
+assert(apiClients['a'].id)
+```
+
+
+# .log
+.log.tail.
+
+```js
+done => {
+ apiClients['a'].log.tail
+ if (err) {
+ throw err
+ }
+ res.once('data', obj => {
+ assert(obj)
+ assert.equal(typeof obj, 'object')
+ done()
+ })
+ })
+```
+
+
+# .name
+.name.publish.
+
+```js
+done => {
+ apiClients['a'].name.publish
+ if (err) {
+ throw err
+ }
+ assert(res)
+ name = res
+ done()
+ })
+```
+
+.name.resolve.
+
+```js
+done => {
+ apiClients['a'].name.resolve
+ if (err) {
+ throw err
+ }
+ assert(res)
+ assert.deepEqual(res, { Path: '/ipfs/' + name.Value })
+ done()
+ })
+```
+
+
+# .object
+object.put.
+
+```js
+done => {
+ apiClients['a'].object.put
+ if (err) throw err
+ const obj = res
+ assert.equal(obj.Hash, testObjectHash)
+ assert.equal(obj.Links.length, 0)
+ done()
+ })
+```
+
+object.get.
+
+```js
+done => {
+ apiClients['a'].object.get
+ if (err) {
+ throw err
+ }
+ const obj = res
+ assert.equal(obj.Data, 'testdata')
+ assert.equal(obj.Links.length, 0)
+ done()
+ })
+```
+
+object.data.
+
+```js
+done => {
+ apiClients['a'].object.data
+ if (err) throw err
+ let buf = ''
+ res
+ .on('error', err => { throw err })
+ .on('data', data => buf += data)
+ .on('end', () => {
+ assert.equal(buf, 'testdata')
+ done()
+ })
+ })
+```
+
+object.stat.
+
+```js
+done => {
+ apiClients['a'].object.stat
+ if (err) {
+ throw err
+ }
+ assert.deepEqual(res, {
+ Hash: 'QmPTkMuuL6PD8L2SwTwbcs1NPg14U8mRzerB1ZrrBrkSDD',
+ NumLinks: 0,
+ BlockSize: 10,
+ LinksSize: 2,
+ DataSize: 8,
+ CumulativeSize: 10
+ })
+ done()
+ })
+```
+
+object.links.
+
+```js
+done => {
+ apiClients['a'].object.links
+ if (err) {
+ throw err
+ }
+ assert.deepEqual(res, {
+ Hash: 'QmPTkMuuL6PD8L2SwTwbcs1NPg14U8mRzerB1ZrrBrkSDD',
+ Links: []
+ })
+ done()
+ })
+```
+
+object.patch.
+
+```js
+done => {
+ apiClients['a'].object.put
+ if (err) {
+ throw err
+ }
+ apiClients['a'].object.patch(testObjectHash, ['add-link', 'next', testPatchObjectHash], (err, res) => {
+ if (err) {
+ throw err
+ }
+ assert.deepEqual(res, {
+ Hash: 'QmZFdJ3CQsY4kkyQtjoUo8oAzsEs5BNguxBhp8sjQMpgkd',
+ Links: null
+ })
+ apiClients['a'].object.get(res.Hash, (err, res2) => {
+ if (err) {
+ throw err
+ }
+ assert.deepEqual(res2, {
+ Data: 'testdata',
+ Links: [{
+ Name: 'next',
+ Hash: 'QmWJDtdQWQSajQPx1UVAGWKaSGrHVWdjnrNhbooHP7LuF2',
+ Size: 15
+ }]
+ })
+ done()
+ })
+ })
+ })
+```
+
+
+# .pin
+.pin.add.
+
+```js
+done => {
+ apiClients['b'].pin.add
+ if (err) {
+ throw err
+ }
+ assert.equal(res.Pinned[0], 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP')
+ done()
+ })
+```
+
+.pin.list.
+
+```js
+done => {
+ apiClients['b'].pin.list
+ if (err) {
+ throw err
+ }
+ assert(res)
+ done()
+ })
+```
+
+.pin.remove.
+
+```js
+done => {
+ apiClients['b'].pin.remove
+ if (err) {
+ throw err
+ }
+ assert(res)
+ apiClients['b'].pin.list('direct', (err, res) => {
+ if (err) {
+ throw err
+ }
+ assert(res)
+ assert.equal(Object.keys(res.Keys).length, 0)
+ done()
+ })
+ })
+```
+
+
+# .ping
+ping another peer.
+
+```js
+done => {
+ // TODO remove this when https://github.com/ipfs/js-ipfs-api/issues/135 is resolved
+ if (!isNode) {
+ return done()
+ }
+ apiClients['b'].id
+ if (err) {
+ throw err
+ }
+ apiClients['a'].ping(id.ID, (err, res) => {
+ if (err) {
+ throw err
+ }
+ assert(res)
+ assert(res.Success)
+ done()
+ })
+ })
+```
+
+
+# .refs
+refs.
+
+```js
+done => {
+ if (!isNode) {
+ return done()
+ }
+ apiClients['a'].refs
+ if (err) {
+ throw err
+ }
+ const result = [{
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmcUYKmQxmTcFom4R4UZP7FWeQzgJkwcFn51XrvsMy7PE9 add.js',
+ Err: ''
+ }, {
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmNeHxDfQfjVFyYj2iruvysLH9zpp78v3cu1s3BZq1j5hY cat.js',
+ Err: ''
+ }, {
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmTYFLz5vsdMpq4XXw1a1pSxujJc9Z5V3Aw1Qg64d849Zy files',
+ Err: ''
+ }, {
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmU7wetVaAqc3Meurif9hcYBHGvQmL5QdpPJYBoZizyTNL ipfs-add.js',
+ Err: ''
+ }, {
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmctZfSuegbi2TMFY2y3VQjxsH5JbRBu7XmiLfHNvshhio ls.js',
+ Err: ''
+ }, {
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmTDH2RXGn8XyDAo9YyfbZAUXwL1FCr44YJCN9HBZmL9Gj test-folder',
+ Err: ''
+ }, {
+ Ref: 'QmSzLpCVbWnEm3XoTWnv6DT6Ju5BsVoLhzvxKXZeQ2cmdg QmbkMNB6rwfYAxRvnG9CWJ6cKKHEdq2ZKTozyF5FQ7H8Rs version.js',
+ Err: ''
+ }]
+ assert.deepEqual(objs, result)
+ done()
+ })
+```
+
+
+# .send
+
+# .swarm
+.swarm.peers.
+
+```js
+done => {
+ apiClients['a'].swarm.peers
+ if (err) {
+ throw err
+ }
+ assert(res.Strings.length >= 2)
+ done()
+ })
+```
+
+.swarm.connect.
+
+```js
+done => {
+ // Done in the 'before' segment
+ done()
+```
+
+
+# .update (currently disabled, wait for IPFS 0.4.0)
+
+# .version
+checks the version.
+
+```js
+done => {
+ apiClients['a'].version
+ if (err) {
+ throw err
+ }
+ assert(res)
+ assert(res.Version)
+ console.log(' - running against version', res.Version)
+ done()
+ })
+```
+
diff --git a/README.md b/README.md
index a62691578..d4996bce5 100644
--- a/README.md
+++ b/README.md
@@ -64,14 +64,18 @@ ipfs daemon
## API
-### Level 1 Commands
-Level 1 commands are simple commands
+You can find more detailed documentation with examples at [API.md](/API.md).
+
+
+
+
+-----------------------------------------------------------------------------
+
+### Previous API Docs
#### add
-Add a file (where file is any data) to ipfs returning the hash and name. The
-name value will only be set if you are actually sending a file. A single or
-array of files can be used.
+Add a file (where file is any data) to ipfs returning the hash and name. The name value will only be set if you are actually sending a file. A single or array of files can be used.
**Usage**
```javascript
@@ -84,8 +88,7 @@ ipfs.add(files, function(err, res) {
})
})
```
-`files` can be a mixed array of filenames or buffers of data. A single value is
-also acceptable.
+`files` can be a mixed array of filenames or buffers of data. A single value is also acceptable.
Example
```js
@@ -185,9 +188,6 @@ curl "http://localhost:5001/api/v0/ls?arg=&stream-channels=true"
**commands**
-### Level 2 Commands
-Level 2 commands are simply named spaced wrapped commands
-
#### Config
#### Update
diff --git a/examples/add.js b/examples/add.js
deleted file mode 100644
index be3fdedd4..000000000
--- a/examples/add.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict'
-
-var ipfs = require('../src')('localhost', 5001)
-
-var f1 = 'Hello'
-var f2 = 'World'
-
-ipfs.add([new Buffer(f1), new Buffer(f2)], function (err, res) {
- if (err || !res) return console.log(err)
-
- for (var i = 0; i < res.length; i++) {
- console.log(res[i])
- }
-})
-
-ipfs.add(['./files/hello.txt', './files/ipfs.txt'], function (err, res) {
- if (err || !res) return console.log(err)
-
- for (var i = 0; i < res.length; i++) {
- console.log(res[i])
- }
-})
diff --git a/examples/cat.js b/examples/cat.js
deleted file mode 100644
index d24aa5a2d..000000000
--- a/examples/cat.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict'
-
-var ipfs = require('../src')('localhost', 5001)
-
-var hash = [
- 'QmdFyxZXsFiP4csgfM5uPu99AvFiKH62CSPDw5TP92nr7w',
- 'QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu'
-]
-
-ipfs.cat(hash, function (err, res) {
- if (err || !res) return console.log(err)
-
- if (res.readable) {
- res.pipe(process.stdout)
- } else {
- console.log(res)
- }
-})
diff --git a/examples/files/hello.txt b/examples/files/hello.txt
deleted file mode 100644
index e965047ad..000000000
--- a/examples/files/hello.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hello
diff --git a/examples/files/ipfs.txt b/examples/files/ipfs.txt
deleted file mode 100644
index 95a311652..000000000
--- a/examples/files/ipfs.txt
+++ /dev/null
@@ -1 +0,0 @@
-IPFS
diff --git a/examples/ipfs-add.js b/examples/ipfs-add.js
deleted file mode 100755
index 572791db2..000000000
--- a/examples/ipfs-add.js
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env node
-
-var ipfs = require('../src')('localhost', 5001)
-var files = process.argv.slice(2)
-
-ipfs.add(files, {recursive: true}, function (err, res) {
- if (err || !res) return console.log(err)
-
- for (var i = 0; i < res.length; i++) {
- console.log('added', res[i].Hash, res[i].Name)
- }
-})
diff --git a/examples/ls.js b/examples/ls.js
deleted file mode 100644
index 6006e8696..000000000
--- a/examples/ls.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict'
-
-var ipfs = require('../src')('localhost', 5001)
-
-var hash = ['QmdbHK6gMiecyjjSoPnfJg6iKMF7v6E2NkoBgGpmyCoevh']
-
-ipfs.ls(hash, function (err, res) {
- if (err || !res) return console.log(err)
-
- res.Objects.forEach(function (node) {
- console.log(node.Hash)
-
- console.log('Links [%d]', node.Links.length)
- node.Links.forEach(function (link, i) {
- console.log('[%d]', i, link)
- })
- })
-})
diff --git a/examples/version.js b/examples/version.js
deleted file mode 100644
index c69598525..000000000
--- a/examples/version.js
+++ /dev/null
@@ -1,8 +0,0 @@
-'use strict'
-
-var ipfs = require('../src')('localhost', 5001)
-
-ipfs.commands(function (err, res) {
- if (err) throw err
- console.log(res)
-})
diff --git a/package.json b/package.json
index 24aaf5f6d..b8c419e7e 100644
--- a/package.json
+++ b/package.json
@@ -38,9 +38,9 @@
"gulp-filter": "^3.0.1",
"gulp-git": "^1.6.0",
"gulp-load-plugins": "^1.0.0",
- "gulp-mocha": "^2.1.3",
"gulp-size": "^2.0.0",
"gulp-tag-version": "^1.3.0",
+ "gulp-spawn-mocha": "^2.2.1",
"gulp-util": "^3.0.7",
"https-browserify": "0.0.1",
"ipfsd-ctl": "^0.6.1",
diff --git a/tasks/test.js b/tasks/test.js
index 0c32dfb16..9fa4734cb 100644
--- a/tasks/test.js
+++ b/tasks/test.js
@@ -26,6 +26,15 @@ gulp.task('test:node', done => {
)
})
+gulp.task('docs', done => {
+ runSequence(
+ 'daemons:start',
+ 'mocha:docs',
+ 'daemons:stop',
+ done
+ )
+})
+
gulp.task('test:browser', done => {
runSequence(
'daemons:start',
@@ -39,10 +48,20 @@ gulp.task('mocha', () => {
return gulp.src([
'test/setup.js',
'test/**/*.spec.js'
- ])
- .pipe($.mocha({
- timeout: config.webpack.dev.timeout
- }))
+ ]).pipe($.spawnMocha({
+ timeout: config.webpack.dev.timeout
+ }))
+})
+
+gulp.task('mocha:docs', function () {
+ return gulp.src([
+ 'test/setup.js',
+ 'test/**/*.spec.js'
+ ]).pipe($.spawnMocha({
+ timeout: config.webpack.dev.timeout,
+ reporter: 'markdown',
+ output: 'API.md'
+ }))
})
gulp.task('karma', done => {