diff --git a/.gitignore b/.gitignore index 907c78a7..18802421 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ build/Release node_modules dist +docs diff --git a/.npmignore b/.npmignore index 59a480e2..00a160d3 100644 --- a/.npmignore +++ b/.npmignore @@ -26,4 +26,4 @@ build/Release # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules -test \ No newline at end of file +test diff --git a/README.md b/README.md index c9989cce..76e09142 100644 --- a/README.md +++ b/README.md @@ -118,146 +118,7 @@ $ node ## API -```js -const multiaddr = require('multiaddr') -``` - -### Create - -#### const addr = multiaddr(str) - -Creates a multiaddress from a string (e.g. `/ip4/127.0.0.1/udp/1234`). - -#### const addr = multiaddr(buf) - -Creates a multiaddress from another multiaddress' raw bytes. - -#### addr.buffer - -The raw bytes representing this multiaddress. - -#### addr.toString() - -The multiaddress in string format (e.g. `/ip4/127.0.0.1/udp/1234`). - -### Protocols - -#### addr.protoCodes() - -Returns the codes of the protocols in the multiaddress, in left-to-right order. - -```js -addr.protoCodes() -// [4, 6] -``` - -#### addr.protoNames() - -Returns the names of the protocols in the multiaddress, in left-to-right order. - -```js -addr.protoNames() -// ['ip4', 'tcp'] -``` - -#### addr.protos() - -Returns description objects of the protocols in the multiaddress, in left-to-right order. - -```js -addr.protos() -// [ -// { code: 4, name: 'ip4', size: 32}, -// { code: 17, name: 'udp', size: 16} -// ] -``` - -Each object contains the protocol code, protocol name, and the size of its -address space in bits. - -### Node-Friendly Addresses - -Utility functions for getting NodeJS-friendly address information from a -multiaddress. - -#### addr.nodeAddress() - -Returns a NodeJS-friendly object describing the left-most address in the -multiaddress. - -```js -addr.nodeAddress() -// { family: "IPv4", port:1234, address: "127.0.0.1" } -``` - -Note that protocol information is left out: in Node (and most network systems) -the protocol is unknowable given only the address. - -#### addr.fromNodeAddress(addr) - -Constructs a multiaddress, given a NodeJS-friendly address object and a protocol. - -```js -addr.fromNodeAddress({family: "IPv4", port:1234, address: "127.0.0.1"}, 'udp') -// -``` - -#### addr.fromStupidString(str) - -Returns a multiaddress, given a URI in the format `://[:]` - -```js -addr = multiaddr.fromStupidString("udp4://127.0.0.1:1234") -// -``` - -*NOT IMPLEMENTED* - -#### addr.toStupidString() - -*NOT IMPLEMENTED* - -### En/decapsulate - -#### addr.encapsulate(str) - -Returns a new multiaddress that encapsulates `addr` in a new protocol string, -`str`. - -```js -addr.encapsulate('/sctp/5678') -// -``` - -#### addr.decapsulate(str) - -Returns a new multiaddress with the right-most protocol string `str` removed. - -```js -multiaddress('/ip4/127.0.0.1/udp/1234').decapsulate('/udp') -// -``` - -### Tunneling - -Given these encapsulation/decapsulate tools, multiaddresses lend -themselves to expressing tunnels very nicely: - -```js -const printer = multiaddr('/ip4/192.168.0.13/tcp/80') - -const proxy = multiaddr('/ip4/10.20.30.40/tcp/443') - -const printerOverProxy = proxy.encapsulate(printer) -// -``` - -### Misc - -#### `multiaddr.isMultiaddr(addr)` - -Returns `true` if the passed in `addr` is a valid `multiaddr`. +TODO: Moved to API-docs, insert link here ## Maintainers diff --git a/documentation.yml b/documentation.yml new file mode 100644 index 00000000..63ff7196 --- /dev/null +++ b/documentation.yml @@ -0,0 +1,3 @@ +toc: + - name: Introduction + file: intro.md diff --git a/intro.md b/intro.md new file mode 100644 index 00000000..3dd9c11f --- /dev/null +++ b/intro.md @@ -0,0 +1,84 @@ +JavaScript implementation of [Multiaddr](https://github.com/multiformats/multiaddr). + +## What is multiaddr? + +Multiaddr is a standard way to represent addresses that: +- Support any standard network protocols. +- Self-describe (include protocols). +- Have a binary packed format. +- Have a nice string representation. +- Encapsulate well. + +You can read more about what Multiaddr is in the language-independent Github repository: +https://github.com/multiformats/multiaddr + +Multiaddr is a part of a group of values called [Multiformats](https://github.com/multiformats/multiformats) + +## Example + +```js +var Multiaddr = require('multiaddr') + +var home = new Multiaddr('/ip4/127.0.0.1/tcp/80') +// + +home.buffer +// + +home.toString() +// '/ip4/127.0.0.1/tcp/80' + +home.protos() +// [ { code: 4, size: 32, name: 'ip4' }, +// { code: 6, size: 16, name: 'tcp' } ] + +home.nodeAddress() +// { family: 'IPv4', address: '127.0.0.1', port: '80' } + +var proxy = new Multiaddr('/ip4/192.168.2.1/tcp/3128') +// + +var full = proxy.encapsulate(home) +// + +full.toString() +// '/ip4/192.168.2.1/tcp/3128/ip4/127.0.0.1/tcp/80' +``` + +## Installation + +### npm + +```sh +> npm install multiaddr +``` + +## Setup + +### Node.js + +```js +var Multiaddr = require('multiaddr') +``` + +### Browser: Browserify, Webpack, other bundlers + +The code published to npm that gets loaded on require is in fact a ES5 +transpiled version with the right shims added. This means that you can require +it and use with your favourite bundler without having to adjust asset management +process. + +```js +var Multiaddr = require('multiaddr') +``` + +### Browser: ` + + +``` diff --git a/package.json b/package.json index a058d74d..386fb8aa 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "release-minor": "aegir-release --type minor", "release-major": "aegir-release --type major", "coverage": "aegir-coverage", - "coverage-publish": "aegir-coverage publish" + "coverage-publish": "aegir-coverage publish", + "docs": "aegir-docs" }, "pre-commit": [ "lint", @@ -44,7 +45,7 @@ "xtend": "^4.0.1" }, "devDependencies": { - "aegir": "^9.1.2", + "aegir": "^9.3.0", "buffer-loader": "0.0.1", "chai": "^3.5.0", "pre-commit": "^1.1.3" @@ -58,4 +59,4 @@ "Stephen Whitmore ", "npm-to-cdn-bot (by Forbes Lindesay) " ] -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 3bf18d81..5917729c 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,17 @@ var varint = require('varint') exports = module.exports = Multiaddr +/** + * Creates a [multiaddr](https://github.com/multiformats/multiaddr) from + * a Buffer, String or another Multiaddr instance + * public key. + * @class Multiaddr + * @param {(String|Buffer|Multiaddr)} addr - If String or Buffer, needs to adhere + * to the address format of a [multiaddr](https://github.com/multiformats/multiaddr#string-format) + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001') + * // + */ function Multiaddr (addr) { if (!(this instanceof Multiaddr)) { return new Multiaddr(addr) @@ -20,6 +31,9 @@ function Multiaddr (addr) { } if (addr instanceof Buffer) { + /** + * @type {Buffer} - The raw bytes representing this multiaddress + */ this.buffer = codec.fromBuffer(addr) } else if (typeof (addr) === 'string' || addr instanceof String) { this.buffer = codec.fromString(addr) @@ -30,12 +44,26 @@ function Multiaddr (addr) { } } -// get the multiaddr protocols +/** + * Returns Multiaddr as a String + * + * @returns {String} + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').toString() + * // '/ip4/127.0.0.1/tcp/4001' + */ Multiaddr.prototype.toString = function toString () { return codec.bufferToString(this.buffer) } -// get the multiaddr as a convinent options object to be dropped in net.createConnection +/** + * Returns Multiaddr as a convinient options object to be used with net.createConnection + * + * @returns {{family: String, host: String, transport: String, port: String}} + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').toOptions() + * // { family: 'ipv4', host: '127.0.0.1', transport: 'tcp', port: '4001' } + */ Multiaddr.prototype.toOptions = function toOptions () { var opts = {} var parsed = this.toString().split('/') @@ -46,14 +74,35 @@ Multiaddr.prototype.toOptions = function toOptions () { return opts } -// get the multiaddr protocols +/** + * Returns Multiaddr as a human-readable string + * + * @returns {String} + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').inspect() + * // '' + */ Multiaddr.prototype.inspect = function inspect () { return '' } -// get the multiaddr protocols +/** + * Returns the protocols the Multiaddr is defined with, as an array of objects, in + * left-to-right order. Each object contains the protocol code, protocol name, + * and the size of its address space in bits. + * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv) + * + * @returns {Array.} protocols - All the protocols the address is composed of + * @returns {Number} protocols[].code + * @returns {Number} protocols[].size + * @returns {String} protocols[].name + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').protos() + * // [ { code: 4, size: 32, name: 'ip4' }, + * // { code: 6, size: 16, name: 'tcp' } ] + */ Multiaddr.prototype.protos = function protos () { return map(this.protoCodes(), function (code) { return extend(protocols(code)) @@ -61,7 +110,15 @@ Multiaddr.prototype.protos = function protos () { }) } -// get the multiaddr protocol codes +/** + * Returns the codes of the protocols in left-to-right order. + * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv) + * + * @returns {Array.} protocol codes + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').protoCodes() + * // [ 4, 6 ] + */ Multiaddr.prototype.protoCodes = function protoCodes () { const codes = [] const buf = this.buffer @@ -80,29 +137,91 @@ Multiaddr.prototype.protoCodes = function protoCodes () { return codes } -// get the multiaddr protocol string names +/** + * Returns the names of the protocols in left-to-right order. + * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv) + * + * @return {Array.} protocol names + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').protoNames() + * // [ 'ip4', 'tcp' ] + */ Multiaddr.prototype.protoNames = function protoNames () { return map(this.protos(), function (proto) { return proto.name }) } -// Returns a tuple of parts: +/** + * Returns a tuple of parts + * + * @return {Array.} tuples + * @return {Number} tuples[].0 code of protocol + * @return {Buffer} tuples[].1 contents of address + * @example + * Multiaddr("/ip4/127.0.0.1/tcp/4001").tuples() + * // [ [ 4, ], [ 6, ] ] + */ Multiaddr.prototype.tuples = function tuples () { return codec.bufferToTuples(this.buffer) } -// Returns a tuple of string parts: +/** + * Returns a tuple of string/number parts + * + * @return {Array.} tuples + * @return {Number} tuples[].0 code of protocol + * @return {(String|Number)} tuples[].1 contents of address + * @example + * Multiaddr("/ip4/127.0.0.1/tcp/4001").stringTuples() + * // [ [ 4, '127.0.0.1' ], [ 6, 4001 ] ] + */ Multiaddr.prototype.stringTuples = function stringTuples () { var t = codec.bufferToTuples(this.buffer) return codec.tuplesToStringTuples(t) } +/** + * Encapsulates a Multiaddr in another Multiaddr + * + * @param {Multiaddr} addr - Multiaddr to add into this Multiaddr + * @return {Multiaddr} + * @example + * var mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080') + * // + * + * var mh2 = Multiaddr('/ip4/127.0.0.1/tcp/4001') + * // + * + * var mh3 = mh1.encapsulate(mh2) + * // + * + * mh3.toString() + * // '/ip4/8.8.8.8/tcp/1080/ip4/127.0.0.1/tcp/4001' + */ Multiaddr.prototype.encapsulate = function encapsulate (addr) { addr = Multiaddr(addr) return Multiaddr(this.toString() + addr.toString()) } +/** + * Decapsulates a Multiaddr from another Multiaddr + * + * @param {Multiaddr} addr - Multiaddr to remove from this Multiaddr + * @return {Multiaddr} + * @example + * var mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080') + * // + * + * var mh2 = Multiaddr('/ip4/127.0.0.1/tcp/4001') + * // + * + * var mh3 = mh1.encapsulate(mh2) + * // + * + * mh3.decapsulate(mh2).toString() + * // '/ip4/8.8.8.8/tcp/1080' + */ Multiaddr.prototype.decapsulate = function decapsulate (addr) { addr = addr.toString() var s = this.toString() @@ -113,11 +232,41 @@ Multiaddr.prototype.decapsulate = function decapsulate (addr) { return Multiaddr(s.slice(0, i)) } +/** + * Checks if two Multiaddrs are the same + * + * @param {Multiaddr} addr + * @return {Bool} + * @example + * var mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080') + * // + * + * var mh2 = Multiaddr('/ip4/127.0.0.1/tcp/4001') + * // + * + * mh1.equals(mh1) + * // true + * + * mh1.equals(mh2) + * // false + */ Multiaddr.prototype.equals = function equals (addr) { return this.buffer.equals(addr.buffer) } -// get a node friendly address object +/** + * Gets a Multiaddrs node-friendly address object. Note that protocol information + * is left out: in Node (and most network systems) the protocol is unknowable + * given only the address. + * + * Has to be a ThinWaist Address, otherwise throws error + * + * @returns {{family: String, address: String, port: String}} + * @throws {Error} Throws error if Multiaddr is not a Thin Waist address + * @example + * Multiaddr('/ip4/127.0.0.1/tcp/4001').nodeAddress() + * // {family: 'IPv4', address: '127.0.0.1', port: '4001'} + */ Multiaddr.prototype.nodeAddress = function nodeAddress () { if (!this.isThinWaistAddress()) { throw new Error('Multiaddr must be "thin waist" address for nodeAddress.') @@ -132,7 +281,18 @@ Multiaddr.prototype.nodeAddress = function nodeAddress () { } } -// from a node friendly address object +/** + * Creates a Multiaddr from a node-friendly address object + * + * @param {String} addr + * @param {String} transport + * @returns {Multiaddr} multiaddr + * @throws {Error} Throws error if addr is not truthy + * @throws {Error} Throws error if transport is not truthy + * @example + * Multiaddr.fromNodeAddress({address: '127.0.0.1', port: '4001'}, 'tcp') + * // + */ Multiaddr.fromNodeAddress = function fromNodeAddress (addr, transport) { if (!addr) throw new Error('requires node address object') if (!transport) throw new Error('requires transport protocol') @@ -140,8 +300,30 @@ Multiaddr.fromNodeAddress = function fromNodeAddress (addr, transport) { return Multiaddr('/' + [ip, addr.address, transport, addr.port].join('/')) } -// returns whether this address is a standard combination: -// /{IPv4, IPv6}/{TCP, UDP} +// TODO find a better example, not sure about it's good enough +/** + * Returns if a Multiaddr is a Thin Waist address or not. + * + * Thin Waist is if a Multiaddr adheres to the standard combination of: + * + * `{IPv4, IPv6}/{TCP, UDP}` + * + * @param {Multiaddr} [addr] - Defaults to using `this` instance + * @returns {Boolean} isThinWaistAddress + * @example + * const mh1 = Multiaddr('/ip4/127.0.0.1/tcp/4001') + * // + * const mh2 = Multiaddr('/ip4/192.168.2.1/tcp/5001') + * // + * const mh3 = mh1.encapsulate(mh2) + * // + * mh1.isThinWaistAddress() + * // true + * mh2.isThinWaistAddress() + * // true + * mh3.isThinWaistAddress() + * // false + */ Multiaddr.prototype.isThinWaistAddress = function isThinWaistAddress (addr) { var protos = (addr || this).protos() @@ -158,16 +340,49 @@ Multiaddr.prototype.isThinWaistAddress = function isThinWaistAddress (addr) { return true } -// parses the "stupid string" format: -// ://[:] -// udp4://1.2.3.4:5678 +// TODO rename this to something else than "stupid string" +/** + * Converts a "stupid string" into a Multiaddr. + * + * Stupid string format: + * ``` + * ://[:] + * udp4://1.2.3.4:5678 + * ``` + * + * @param {String} [str] - String in the "stupid" format + * @throws {NotImplemented} + * @returns {undefined} + * @todo Not Implemented yet + */ Multiaddr.prototype.fromStupidString = function fromStupidString (str) { throw NotImplemented } -// patch this in +/** + * Object containing table, names and codes of all supported protocols. + * To get the protocol values from a Multiaddr, you can use + * [`.protos()`](#multiaddrprotos), + * [`.protoCodes()`](#multiaddrprotocodes) or + * [`.protoNames()`](#multiaddrprotonames) + * + * @instance + * @returns {{table: Array, names: Object, codes: Object}} + * + */ Multiaddr.protocols = protocols +/** + * Returns if something is a Multiaddr or not + * + * @param {Multiaddr} addr + * @return {Bool} isMultiaddr + * @example + * Multiaddr.isMultiaddr(Multiaddr('/ip4/127.0.0.1/tcp/4001')) + * // true + * Multiaddr.isMultiaddr('/ip4/127.0.0.1/tcp/4001') + * // false + */ Multiaddr.isMultiaddr = function isMultiaddr (addr) { if (addr.constructor && addr.constructor.name) { return addr.constructor.name === 'Multiaddr'