diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..dd84ea78
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..bbcbbe7d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..b94c65e7
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,67 @@
+name: ci
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: yarn
+ - run: yarn lint
+ - run: yarn build
+ - uses: gozala/typescript-error-reporter-action@v1.0.4
+ - run: yarn aegir dep-check -- -i aegir
+ - uses: ipfs/aegir/actions/bundle-size@master
+ name: size
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ test-node:
+ needs: check
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [windows-latest, ubuntu-latest, macos-latest]
+ node: [12, 14]
+ fail-fast: true
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node }}
+ - run: yarn
+ - run: npx nyc --reporter=lcov npm run test:node -- --bail
+ - uses: codecov/codecov-action@v1
+ test-chrome:
+ needs: check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: yarn
+ - run: yarn aegir test -t browser -t webworker
+ test-firefox:
+ needs: check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: yarn
+ - run: yarn aegir test -t browser -t webworker -- --browsers FirefoxHeadless
+ test-electron-main:
+ needs: check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: yarn
+ - run: npx xvfb-maybe yarn aegir test -t electron-main --bail
+ test-electron-renderer:
+ needs: check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - run: yarn
+ - run: npx xvfb-maybe yarn aegir test -t electron-renderer --bail
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 81758d88..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-language: node_js
-cache: npm
-stages:
- - check
- - test
- - cov
-
-node_js:
- - 'lts/*'
- - 'stable'
-
-os:
- - linux
- - osx
-
-script: npx nyc -s npm run test:node -- --bail
-after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
-
-jobs:
- include:
- - os: windows
- cache: false
-
- - stage: check
- script:
- - npx aegir dep-check
- - npm run lint
- - npm run test:types
-
- - stage: test
- name: chrome
- addons:
- chrome: stable
- script: npx aegir test -t browser -t webworker
-
- - stage: test
- name: firefox
- addons:
- firefox: latest
- script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
-
-notifications:
- email: false
-
diff --git a/README.md b/README.md
index cf037f0e..56b4f177 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,31 @@
-js-multiaddr
+js-multiaddr
============
-[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
-[![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
-[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
-[![Dependency Status](https://david-dm.org/multiformats/js-multiaddr.svg?style=flat-square)](https://david-dm.org/multiformats/js-multiaddr)
-[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
-[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
-[![](https://img.shields.io/travis/multiformats/js-multiaddr.svg?style=flat-square)](https://travis-ci.com/multiformats/js-multiaddr)
+[![pl](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai)
+[![project](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
+[![irc](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
[![codecov](https://img.shields.io/codecov/c/github/multiformats/js-multiaddr.svg?style=flat-square)](https://codecov.io/gh/multiformats/js-multiaddr)
+[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/multiformats/js-multiaddr/ci?label=ci&style=flat-square)](https://github.com/multiformats/js-multiaddr/actions?query=branch%3Amaster+workflow%3Aci+)
> JavaScript implementation of [multiaddr](https://github.com/multiformats/multiaddr).
-## Lead Maintainer
+## Lead Maintainer
[Jacob Heun](https://github.com/jacobheun)
-## Table of Contents
-
-- [js-multiaddr](#js-multiaddr)
- - [Lead Maintainer](#lead-maintainer)
- - [Table of Contents](#table-of-contents)
- - [Background](#background)
- - [What is multiaddr?](#what-is-multiaddr)
- - [Install](#install)
- - [Setup](#setup)
- - [Node.js](#nodejs)
- - [Browser: Browserify, Webpack, other bundlers](#browser-browserify-webpack-other-bundlers)
- - [Browser: `
-
-
-```
diff --git a/package.json b/package.json
index 637dae53..a58273e8 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,21 @@
"description": "multiaddr implementation (binary + string representation of network addresses)",
"leadMaintainer": "Jacob Heun ",
"main": "src/index.js",
- "types": "src/index.d.ts",
+ "types": "dist/src/index.d.ts",
+ "typesVersions": {
+ "*": {
+ "src/*": [
+ "dist/src/*",
+ "dist/src/*/index"
+ ]
+ }
+ },
"scripts": {
"lint": "aegir lint",
- "test": "npm run test:node && npm run test:browser",
- "test:node": "aegir test --ts -t node",
+ "test": "aegir test",
+ "test:node": "aegir test -t node",
"test:browser": "aegir test -t browser",
- "test:types": "npx tsc",
+ "test:types": "aegir ts -p check",
"build": "aegir build",
"release": "aegir release",
"release-minor": "aegir release --type minor",
@@ -42,16 +50,14 @@
"is-ip": "^3.1.0",
"multibase": "^3.0.0",
"uint8arrays": "^1.1.0",
- "varint": "^5.0.0"
+ "varint": "^6.0.0"
},
"devDependencies": {
- "@types/chai": "^4.2.8",
- "@types/dirty-chai": "^2.0.2",
- "@types/mocha": "^8.0.1",
- "@types/node": "^14.0.11",
- "aegir": "^29.0.1",
- "sinon": "^9.2.0",
- "typescript": "^3.9.5"
+ "aegir": "^29.1.0",
+ "sinon": "^9.2.0"
+ },
+ "eslintConfig": {
+ "extends": "ipfs"
},
"contributors": [
"David Dias ",
diff --git a/src/codec.js b/src/codec.js
index 09bbfa0c..75a245d0 100644
--- a/src/codec.js
+++ b/src/codec.js
@@ -100,11 +100,18 @@ function stringTuplesToTuples (tuples) {
})
}
-// [[int code, Uint8Array]... ] -> [[str name, str addr]... ]
+/**
+ * Convert tuples to string tuples
+ *
+ * [[int code, Uint8Array]... ] -> [[int code, str addr]... ]
+ *
+ * @param {Array<[number, Uint8Array?]>} tuples
+ * @returns {Array<[number, string?]>}
+ */
function tuplesToStringTuples (tuples) {
return tuples.map(tup => {
const proto = protoFromTuple(tup)
- if (tup.length > 1) {
+ if (tup.length > 1 && tup[1]) {
return [proto.code, convert.toString(proto.code, tup[1])]
}
return [proto.code]
@@ -137,7 +144,14 @@ function sizeForAddr (p, addr) {
}
// Uint8Array -> [[int code, Uint8Array ]... ]
+
+/**
+ *
+ * @param {Uint8Array} buf
+ * @returns {Array<[number, Uint8Array?]>}
+ */
function bytesToTuples (buf) {
+ /** @type {Array<[number, Uint8Array?]>} */
const tuples = []
let i = 0
while (i < buf.length) {
diff --git a/src/convert.js b/src/convert.js
index 7258b50c..7af26a6f 100644
--- a/src/convert.js
+++ b/src/convert.js
@@ -20,9 +20,16 @@ function Convert (proto, a) {
}
}
-Convert.toString = function convertToString (proto, buf) {
- proto = protocols(proto)
- switch (proto.code) {
+/**
+ * Convert [code,Uint8Array] to string
+ *
+ * @param {number|string} proto
+ * @param {Uint8Array} buf
+ * @returns {string}
+ */
+Convert.toString = function (proto, buf) {
+ const protocol = protocols(proto)
+ switch (protocol.code) {
case 4: // ipv4
case 41: // ipv6
return bytes2ip(buf)
@@ -31,7 +38,7 @@ Convert.toString = function convertToString (proto, buf) {
case 273: // udp
case 33: // dccp
case 132: // sctp
- return bytes2port(buf)
+ return bytes2port(buf).toString()
case 53: // dns
case 54: // dns4
@@ -137,6 +144,12 @@ function mh2bytes (hash) {
return uint8ArrayConcat([size, mh], size.length + mh.length)
}
+/**
+ * Converts bytes to bas58btc string
+ *
+ * @param {Uint8Array} buf
+ * @returns {string} bas58btc string
+ */
function bytes2mh (buf) {
const size = varint.decode(buf)
const address = buf.slice(varint.decode.bytes)
diff --git a/src/index.d.ts b/src/index.d.ts
deleted file mode 100644
index 0fca50e3..00000000
--- a/src/index.d.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-declare type NetOptions = {
- family: "ipv4" | "ipv6";
- host: string;
- transport: "tcp" | "udp";
- port: number;
-};
-
-declare type Protocol = {
- code: number;
- size: number;
- name: string;
- resolvable: boolean | undefined;
- path: boolean | undefined;
-};
-
-declare interface Protocols {
- table: {
- [index: number]: Protocol;
- };
- names: {
- [index: string]: Protocol;
- };
- codes: {
- [index: number]: Protocol;
- };
- object(
- code: number,
- size: number,
- name: string,
- resolvable?: any,
- path?: any
- ): Protocol;
-}
-
-declare type NodeAddress = {
- family: "IPv4" | "IPv6";
- address: string;
- port: string;
-};
-
-declare type MultiaddrInput = string | Uint8Array | Multiaddr | null;
-
-declare class Multiaddr {
- /**
- * Creates a [multiaddr](https://github.com/multiformats/multiaddr) from
- * a Uint8Array, String or another Multiaddr instance
- * public key.
- * @param addr - If String or Uint8Array, needs to adhere
- * to the address format of a [multiaddr](https://github.com/multiformats/multiaddr#string-format)
- */
- constructor(addr?: MultiaddrInput);
-
- bytes: Uint8Array;
-
- /**
- * Returns Multiaddr as a String
- */
- toString(): string;
-
- /**
- * Returns Multiaddr as a JSON encoded object
- */
- toJSON(): string;
-
- /**
- * Returns Multiaddr as a convinient options object to be used with net.createConnection
- */
- toOptions(): NetOptions;
-
- /**
- * Returns Multiaddr as a human-readable string
- */
- inspect(): string;
-
- /**
- * 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)
- */
- protos(): Protocol[];
-
- /**
- * Returns the codes of the protocols in left-to-right order.
- * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv)
- */
- protoCodes(): number[];
-
- /**
- * Returns the names of the protocols in left-to-right order.
- * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv)
- */
- protoNames(): string[];
-
- /**
- * Returns a tuple of parts
- */
- tuples(): [number, Uint8Array][];
-
- /**
- * Returns a tuple of string/number parts
- */
- stringTuples(): [number, string | number][];
-
- /**
- * Encapsulates a Multiaddr in another Multiaddr
- */
- encapsulate(addr: MultiaddrInput): Multiaddr;
-
- /**
- * Decapsulates a Multiaddr from another Multiaddr
- */
- decapsulate(addr: MultiaddrInput): Multiaddr;
-
- /**
- * A more reliable version of `decapsulate` if you are targeting a
- * specific code, such as 421 (the `p2p` protocol code). The last index of the code
- * will be removed from the `Multiaddr`, and a new instance will be returned.
- * If the code is not present, the original `Multiaddr` is returned.
- */
- decapsulateCode(code: number): Multiaddr;
-
- /**
- * Extract the peerId if the multiaddr contains one
- */
- getPeerId(): string;
-
- /**
- * Extract the path if the multiaddr contains one
- */
- getPath(): string | null;
-
- /**
- * Checks if two Multiaddrs are the same
- */
- equals(addr: Multiaddr): boolean;
-
- /**
- * 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
- */
- nodeAddress(): NodeAddress;
-
- /**
- * 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}`
- */
- isThinWaistAddress(addr?: Multiaddr): boolean;
-
- /**
- * Resolve multiaddr if containing resolvable hostname.
- */
- resolve(): Promise>
-}
-
-declare namespace Multiaddr {
- const resolvers: Map < string, (addr: Multiaddr) => Promise < Array < string >>>
-
- /**
- * Creates a Multiaddr from a node-friendly address object
- */
- function fromNodeAddress(addr: NodeAddress, transport: string): Multiaddr;
-
- /**
- * 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)
- */
- const protocols: Protocols;
-
- /**
- * Returns if something is a Multiaddr
- */
- function isMultiaddr(addr: unknown): addr is Multiaddr;
-
- /**
- * Returns if something is a Multiaddr that is a name
- */
- function isName(addr: Multiaddr): boolean;
-
- /**
- * Returns an array of multiaddrs, by resolving the multiaddr that is a name
- */
- function resolve(addr: Multiaddr): Promise;
-}
-
-/**
- * Creates a [multiaddr](https://github.com/multiformats/multiaddr) from
- * a Uint8Array, String or another Multiaddr instance
- * public key.
- * @param addr - If String or Uint8Array, needs to adhere
- * to the address format of a [multiaddr](https://github.com/multiformats/multiaddr#string-format)
- */
-declare function Multiaddr(input?: MultiaddrInput): Multiaddr;
-
-export = Multiaddr;
diff --git a/src/index.js b/src/index.js
index fc464bd6..1450b411 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,556 +4,582 @@ const codec = require('./codec')
const protocols = require('./protocols-table')
const varint = require('varint')
const CID = require('cids')
-const withIs = require('class-is')
const errCode = require('err-code')
-const inspect = Symbol.for('nodejs.util.inspect.custom')
const uint8ArrayToString = require('uint8arrays/to-string')
const uint8ArrayEquals = require('uint8arrays/equals')
const resolvers = new Map()
+const symbol = Symbol.for('@multiformats/js-multiaddr/multiaddr')
+
+/**
+ * @typedef {import('./types').Protocol} Protocol
+ */
/**
* Creates a [multiaddr](https://github.com/multiformats/multiaddr) from
* a Uint8Array, String or another Multiaddr instance
* public key.
- *
- * @class Multiaddr
- * @param {(string | Uint8Array | Multiaddr)} addr - If String or Uint8Array, 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')
- * //
*/
-const Multiaddr = withIs.proto(function (addr) {
- if (!(this instanceof Multiaddr)) {
- return new Multiaddr(addr)
- }
+class Multiaddr {
+ /**
+ * @param {string | Uint8Array | Multiaddr | null} [addr] - If String or Uint8Array, needs to adhere
+ * to the address format of a [multiaddr](https://github.com/multiformats/multiaddr#string-format)
+ * @example
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001')
+ * //
+ * ```
+ */
+ constructor (addr) {
+ Object.defineProperty(this, symbol, { value: true })
- // default
- if (addr == null) {
- addr = ''
- }
+ // default
+ if (addr == null) {
+ addr = ''
+ }
- if (addr instanceof Uint8Array) {
- /**
- * @type {Uint8Array} - The raw bytes representing this multiaddress
- */
- this.bytes = codec.fromBytes(addr)
- } else if (typeof addr === 'string' || addr instanceof String) {
- if (addr.length > 0 && addr.charAt(0) !== '/') {
- throw new Error(`multiaddr "${addr}" must start with a "/"`)
+ if (addr instanceof Uint8Array) {
+ /**
+ * @type {Uint8Array} - The raw bytes representing this multiaddress
+ */
+ this.bytes = codec.fromBytes(addr)
+ } else if (typeof addr === 'string' || addr instanceof String) {
+ if (addr.length > 0 && addr.charAt(0) !== '/') {
+ throw new Error(`multiaddr "${addr}" must start with a "/"`)
+ }
+ this.bytes = codec.fromString(addr)
+ } else if (addr.bytes && addr.protos && addr.protoCodes) { // Multiaddr
+ this.bytes = codec.fromBytes(addr.bytes) // validate + copy buffer
+ } else {
+ throw new Error('addr must be a string, Buffer, or another Multiaddr')
}
- this.bytes = codec.fromString(addr)
- } else if (addr.bytes && addr.protos && addr.protoCodes) { // Multiaddr
- this.bytes = codec.fromBytes(addr.bytes) // validate + copy buffer
- } else {
- throw new Error('addr must be a string, Buffer, or another Multiaddr')
}
-}, { className: 'Multiaddr', symbolName: '@multiformats/js-multiaddr/multiaddr' })
-/**
- * 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.bytesToString(this.bytes)
-}
-
-/**
- * Returns Multiaddr as a JSON encoded object
- *
- * @returns {string}
- * @example
- * JSON.stringify(Multiaddr('/ip4/127.0.0.1/tcp/4001'))
- * // '/ip4/127.0.0.1/tcp/4001'
- */
-Multiaddr.prototype.toJSON = Multiaddr.prototype.toString
-
-/**
- * Returns Multiaddr as a convinient options object to be used with net.createConnection
- *
- * @returns {{family: string, host: string, transport: string, port: number}}
- * @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 () {
- const opts = {}
- const parsed = this.toString().split('/')
- opts.family = parsed[1] === 'ip4' ? 'ipv4' : 'ipv6'
- opts.host = parsed[2]
- opts.transport = parsed[3]
- opts.port = parseInt(parsed[4])
- return opts
-}
+ /**
+ * Returns Multiaddr as a String
+ *
+ * @returns {string}
+ * @example
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').toString()
+ * // '/ip4/127.0.0.1/tcp/4001'
+ * ```
+ */
+ toString () {
+ return codec.bytesToString(this.bytes)
+ }
-/**
- * Returns Multiaddr as a human-readable string.
- * For post Node.js v10.0.0.
- * https://nodejs.org/api/deprecations.html#deprecations_dep0079_custom_inspection_function_on_objects_via_inspect
- *
- * @returns {string}
- * @example
- * console.log(Multiaddr('/ip4/127.0.0.1/tcp/4001'))
- * // ''
- */
-Multiaddr.prototype[inspect] = function inspectCustom () {
- return ''
-}
+ /**
+ * Returns Multiaddr as a JSON encoded object
+ *
+ * @returns {string}
+ * @example
+ * ```js
+ * JSON.stringify(new Multiaddr('/ip4/127.0.0.1/tcp/4001'))
+ * // '/ip4/127.0.0.1/tcp/4001'
+ * ```
+ */
+ toJSON () {
+ return this.toString()
+ }
-/**
- * Returns Multiaddr as a human-readable string.
- * Fallback for pre Node.js v10.0.0.
- * https://nodejs.org/api/deprecations.html#deprecations_dep0079_custom_inspection_function_on_objects_via_inspect
- *
- * @returns {string}
- * @example
- * Multiaddr('/ip4/127.0.0.1/tcp/4001').inspect()
- * // ''
- */
-Multiaddr.prototype.inspect = function inspect () {
- return ''
-}
+ /**
+ * Returns Multiaddr as a convinient options object to be used with net.createConnection
+ *
+ * @returns {{family: string, host: string, transport: string, port: number}}
+ * @example
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').toOptions()
+ * // { family: 'ipv4', host: '127.0.0.1', transport: 'tcp', port: 4001 }
+ * ```
+ */
+ toOptions () {
+ const opts = {}
+ const parsed = this.toString().split('/')
+ opts.family = parsed[1] === 'ip4' ? 'ipv4' : 'ipv6'
+ opts.host = parsed[2]
+ opts.transport = parsed[3]
+ opts.port = parseInt(parsed[4])
+ return opts
+ }
-/**
- * @typedef {object} protocol
- * @property {number} code
- * @property {number} size
- * @property {string} name
- * @property {boolean} [resolvable]
- * @property {boolean} [path]
- */
+ /**
+ * Returns Multiaddr as a human-readable string.
+ * Fallback for pre Node.js v10.0.0.
+ * https://nodejs.org/api/deprecations.html#deprecations_dep0079_custom_inspection_function_on_objects_via_inspect
+ *
+ * @returns {string}
+ * @example
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').inspect()
+ * // ''
+ * ```
+ */
+ [Symbol.for('nodejs.util.inspect.custom')] () {
+ return ''
+ }
-/**
- * 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 {protocol[]} protocols - All the protocols the address is composed of
- * @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 this.protoCodes().map(code => Object.assign({}, protocols(code)))
-}
+ inspect () {
+ return ''
+ }
-/**
- * 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.bytes
- let i = 0
- while (i < buf.length) {
- const code = varint.decode(buf, i)
- const n = varint.decode.bytes
-
- const p = protocols(code)
- const size = codec.sizeForAddr(p, buf.slice(i + n))
-
- i += (size + n)
- codes.push(code)
+ /**
+ * 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 {Protocol[]} protocols - All the protocols the address is composed of
+ * @example
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').protos()
+ * // [ { code: 4, size: 32, name: 'ip4' },
+ * // { code: 6, size: 16, name: 'tcp' } ]
+ * ```
+ */
+ protos () {
+ return this.protoCodes().map(code => Object.assign({}, protocols(code)))
}
- return 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
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').protoCodes()
+ * // [ 4, 6 ]
+ * ```
+ */
+ protoCodes () {
+ const codes = []
+ const buf = this.bytes
+ let i = 0
+ while (i < buf.length) {
+ const code = varint.decode(buf, i)
+ const n = varint.decode.bytes
+
+ const p = protocols(code)
+ const size = codec.sizeForAddr(p, buf.slice(i + n))
+
+ i += (size + n)
+ codes.push(code)
+ }
-/**
- * Returns the names of the protocols in left-to-right order.
- * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv)
- *
- * @returns {Array.} protocol names
- * @example
- * Multiaddr('/ip4/127.0.0.1/tcp/4001').protoNames()
- * // [ 'ip4', 'tcp' ]
- */
-Multiaddr.prototype.protoNames = function protoNames () {
- return this.protos().map(proto => proto.name)
-}
+ return codes
+ }
-/**
- * Returns a tuple of parts
- *
- * @returns {[number, Uint8Array][]} tuples
- * @example
- * Multiaddr("/ip4/127.0.0.1/tcp/4001").tuples()
- * // [ [ 4, ], [ 6, ] ]
- */
-Multiaddr.prototype.tuples = function tuples () {
- return codec.bytesToTuples(this.bytes)
-}
+ /**
+ * Returns the names of the protocols in left-to-right order.
+ * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv)
+ *
+ * @returns {Array.} protocol names
+ * @example
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').protoNames()
+ * // [ 'ip4', 'tcp' ]
+ * ```
+ */
+ protoNames () {
+ return this.protos().map(proto => proto.name)
+ }
-/**
- * Returns a tuple of string/number parts
- * - tuples[][0] = code of protocol
- * - tuples[][1] = contents of address
- *
- * @returns {[number, string|number][]} tuples
- * @example
- * Multiaddr("/ip4/127.0.0.1/tcp/4001").stringTuples()
- * // [ [ 4, '127.0.0.1' ], [ 6, 4001 ] ]
- */
-Multiaddr.prototype.stringTuples = function stringTuples () {
- const t = codec.bytesToTuples(this.bytes)
- return codec.tuplesToStringTuples(t)
-}
+ /**
+ * Returns a tuple of parts
+ *
+ * @returns {[number, (Uint8Array | undefined)?][]} tuples
+ * @example
+ * ```js
+ * new Multiaddr("/ip4/127.0.0.1/tcp/4001").tuples()
+ * // [ [ 4, ], [ 6, ] ]
+ * ```
+ */
+ tuples () {
+ return codec.bytesToTuples(this.bytes)
+ }
-/**
- * Encapsulates a Multiaddr in another Multiaddr
- *
- * @param {Multiaddr} addr - Multiaddr to add into this Multiaddr
- * @returns {Multiaddr}
- * @example
- * const mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080')
- * //
- *
- * const mh2 = Multiaddr('/ip4/127.0.0.1/tcp/4001')
- * //
- *
- * const 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())
-}
+ /**
+ * Returns a tuple of string/number parts
+ * - tuples[][0] = code of protocol
+ * - tuples[][1] = contents of address
+ *
+ * @returns {[number, string?][]} tuples
+ * @example
+ * ```js
+ * new Multiaddr("/ip4/127.0.0.1/tcp/4001").stringTuples()
+ * // [ [ 4, '127.0.0.1' ], [ 6, '4001' ] ]
+ * ```
+ */
+ stringTuples () {
+ const t = codec.bytesToTuples(this.bytes)
+ return codec.tuplesToStringTuples(t)
+ }
-/**
- * Decapsulates a Multiaddr from another Multiaddr
- *
- * @param {Multiaddr} addr - Multiaddr to remove from this Multiaddr
- * @returns {Multiaddr}
- * @example
- * const mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080')
- * //
- *
- * const mh2 = Multiaddr('/ip4/127.0.0.1/tcp/4001')
- * //
- *
- * const mh3 = mh1.encapsulate(mh2)
- * //
- *
- * mh3.decapsulate(mh2).toString()
- * // '/ip4/8.8.8.8/tcp/1080'
- */
-Multiaddr.prototype.decapsulate = function decapsulate (addr) {
- addr = addr.toString()
- const s = this.toString()
- const i = s.lastIndexOf(addr)
- if (i < 0) {
- throw new Error('Address ' + this + ' does not contain subaddress: ' + addr)
+ /**
+ * Encapsulates a Multiaddr in another Multiaddr
+ *
+ * @param {Multiaddr} addr - Multiaddr to add into this Multiaddr
+ * @returns {Multiaddr}
+ * @example
+ * ```js
+ * const mh1 = new Multiaddr('/ip4/8.8.8.8/tcp/1080')
+ * //
+ *
+ * const mh2 = new Multiaddr('/ip4/127.0.0.1/tcp/4001')
+ * //
+ *
+ * const mh3 = mh1.encapsulate(mh2)
+ * //
+ *
+ * mh3.toString()
+ * // '/ip4/8.8.8.8/tcp/1080/ip4/127.0.0.1/tcp/4001'
+ * ```
+ */
+ encapsulate (addr) {
+ addr = new Multiaddr(addr)
+ return new Multiaddr(this.toString() + addr.toString())
}
- return Multiaddr(s.slice(0, i))
-}
-/**
- * A more reliable version of `decapsulate` if you are targeting a
- * specific code, such as 421 (the `p2p` protocol code). The last index of the code
- * will be removed from the `Multiaddr`, and a new instance will be returned.
- * If the code is not present, the original `Multiaddr` is returned.
- *
- * @param {number} code - The code of the protocol to decapsulate from this Multiaddr
- * @returns {Multiaddr}
- * @example
- * const addr = Multiaddr('/ip4/0.0.0.0/tcp/8080/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC')
- * //
- *
- * addr.decapsulateCode(421).toString()
- * // '/ip4/0.0.0.0/tcp/8080'
- *
- * Multiaddr('/ip4/127.0.0.1/tcp/8080').decapsulateCode(421).toString()
- * // '/ip4/127.0.0.1/tcp/8080'
- */
-Multiaddr.prototype.decapsulateCode = function decapsulateCode (code) {
- const tuples = this.tuples()
- for (let i = tuples.length - 1; i >= 0; i--) {
- if (tuples[i][0] === code) {
- return Multiaddr(codec.tuplesToBytes(tuples.slice(0, i)))
+ /**
+ * Decapsulates a Multiaddr from another Multiaddr
+ *
+ * @param {Multiaddr} addr - Multiaddr to remove from this Multiaddr
+ * @returns {Multiaddr}
+ * @example
+ * ```js
+ * const mh1 = new Multiaddr('/ip4/8.8.8.8/tcp/1080')
+ * //
+ *
+ * const mh2 = new Multiaddr('/ip4/127.0.0.1/tcp/4001')
+ * //
+ *
+ * const mh3 = mh1.encapsulate(mh2)
+ * //
+ *
+ * mh3.decapsulate(mh2).toString()
+ * // '/ip4/8.8.8.8/tcp/1080'
+ * ```
+ */
+ decapsulate (addr) {
+ const addrString = addr.toString()
+ const s = this.toString()
+ const i = s.lastIndexOf(addrString)
+ if (i < 0) {
+ throw new Error('Address ' + this + ' does not contain subaddress: ' + addrString)
}
+ return new Multiaddr(s.slice(0, i))
}
- return this
-}
-/**
- * Extract the peerId if the multiaddr contains one
- *
- * @returns {string | null} peerId - The id of the peer or null if invalid or missing from the ma
- * @example
- * const mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080/ipfs/QmValidBase58string')
- * //
- *
- * // should return QmValidBase58string or null if the id is missing or invalid
- * const peerId = mh1.getPeerId()
- */
-Multiaddr.prototype.getPeerId = function getPeerId () {
- let b58str = null
- try {
- const tuples = this.stringTuples().filter((tuple) => {
- if (tuple[0] === protocols.names.ipfs.code) {
- return true
+ /**
+ * A more reliable version of `decapsulate` if you are targeting a
+ * specific code, such as 421 (the `p2p` protocol code). The last index of the code
+ * will be removed from the `Multiaddr`, and a new instance will be returned.
+ * If the code is not present, the original `Multiaddr` is returned.
+ *
+ * @param {number} code - The code of the protocol to decapsulate from this Multiaddr
+ * @returns {Multiaddr}
+ * @example
+ * ```js
+ * const addr = new Multiaddr('/ip4/0.0.0.0/tcp/8080/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC')
+ * //
+ *
+ * addr.decapsulateCode(421).toString()
+ * // '/ip4/0.0.0.0/tcp/8080'
+ *
+ * new Multiaddr('/ip4/127.0.0.1/tcp/8080').decapsulateCode(421).toString()
+ * // '/ip4/127.0.0.1/tcp/8080'
+ * ```
+ */
+ decapsulateCode (code) {
+ const tuples = this.tuples()
+ for (let i = tuples.length - 1; i >= 0; i--) {
+ if (tuples[i][0] === code) {
+ return new Multiaddr(codec.tuplesToBytes(tuples.slice(0, i)))
}
- })
-
- // Get the last id
- b58str = tuples.pop()[1]
- // Get multihash, unwrap from CID if needed
- b58str = uint8ArrayToString(new CID(b58str).multihash, 'base58btc')
- } catch (e) {
- b58str = null
+ }
+ return this
}
- return b58str
-}
-
-/**
- * Extract the path if the multiaddr contains one
- *
- * @returns {string | null} path - The path of the multiaddr, or null if no path protocol is present
- * @example
- * const mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080/unix/tmp/p2p.sock')
- * //
- *
- * // should return utf8 string or null if the id is missing or invalid
- * const path = mh1.getPath()
- */
-Multiaddr.prototype.getPath = function getPath () {
- let path = null
- try {
- path = this.stringTuples().filter((tuple) => {
- const proto = protocols(tuple[0])
- if (proto.path) {
- return true
+ /**
+ * Extract the peerId if the multiaddr contains one
+ *
+ * @returns {string | null} peerId - The id of the peer or null if invalid or missing from the ma
+ * @example
+ * ```js
+ * const mh1 = new Multiaddr('/ip4/8.8.8.8/tcp/1080/ipfs/QmValidBase58string')
+ * //
+ *
+ * // should return QmValidBase58string or null if the id is missing or invalid
+ * const peerId = mh1.getPeerId()
+ * ```
+ */
+ getPeerId () {
+ try {
+ const tuples = this.stringTuples().filter((tuple) => {
+ if (tuple[0] === protocols.names.ipfs.code) {
+ return true
+ }
+ })
+
+ // Get the last ipfs tuple ['ipfs', 'peerid string']
+ const tuple = tuples.pop()
+ if (tuple && tuple[1]) {
+ // Get multihash, unwrap from CID if needed
+ return uint8ArrayToString(new CID(tuple[1]).multihash, 'base58btc')
+ } else {
+ return null
}
- })[0][1]
- } catch (e) {
- path = null
+ } catch (e) {
+ return null
+ }
}
- return path
-}
-
-/**
- * Checks if two Multiaddrs are the same
- *
- * @param {Multiaddr} addr
- * @returns {Bool}
- * @example
- * const mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080')
- * //
- *
- * const mh2 = Multiaddr('/ip4/127.0.0.1/tcp/4001')
- * //
- *
- * mh1.equals(mh1)
- * // true
- *
- * mh1.equals(mh2)
- * // false
- */
-Multiaddr.prototype.equals = function equals (addr) {
- return uint8ArrayEquals(this.bytes, addr.bytes)
-}
-
-/**
- * Resolve multiaddr if containing resolvable hostname.
- *
- * @returns {Promise>}
- * @example
- * Multiaddr.resolvers.set('dnsaddr', resolverFunction)
- * const mh1 = Multiaddr('/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb')
- * const resolvedMultiaddrs = await mh1.resolve()
- * // [
- * // ,
- * // ,
- * //
- * // ]
- */
-Multiaddr.prototype.resolve = async function resolve () {
- const resolvableProto = this.protos().find((p) => p.resolvable)
+ /**
+ * Extract the path if the multiaddr contains one
+ *
+ * @returns {string | null} path - The path of the multiaddr, or null if no path protocol is present
+ * @example
+ * ```js
+ * const mh1 = new Multiaddr('/ip4/8.8.8.8/tcp/1080/unix/tmp/p2p.sock')
+ * //
+ *
+ * // should return utf8 string or null if the id is missing or invalid
+ * const path = mh1.getPath()
+ * ```
+ */
+ getPath () {
+ let path = null
+ try {
+ path = this.stringTuples().filter((tuple) => {
+ const proto = protocols(tuple[0])
+ if (proto.path) {
+ return true
+ }
+ })[0][1]
+
+ if (!path) {
+ path = null
+ }
+ } catch (e) {
+ path = null
+ }
- // Multiaddr is not resolvable?
- if (!resolvableProto) {
- return [this]
+ return path
}
- const resolver = resolvers.get(resolvableProto.name)
- if (!resolver) {
- throw errCode(new Error(`no available resolver for ${resolvableProto.name}`), 'ERR_NO_AVAILABLE_RESOLVER')
+ /**
+ * Checks if two Multiaddrs are the same
+ *
+ * @param {Multiaddr} addr
+ * @returns {boolean}
+ * @example
+ * ```js
+ * const mh1 = new Multiaddr('/ip4/8.8.8.8/tcp/1080')
+ * //
+ *
+ * const mh2 = new Multiaddr('/ip4/127.0.0.1/tcp/4001')
+ * //
+ *
+ * mh1.equals(mh1)
+ * // true
+ *
+ * mh1.equals(mh2)
+ * // false
+ * ```
+ */
+ equals (addr) {
+ return uint8ArrayEquals(this.bytes, addr.bytes)
}
- const addresses = await resolver(this)
- return addresses.map(a => Multiaddr(a))
-}
+ /**
+ * Resolve multiaddr if containing resolvable hostname.
+ *
+ * @returns {Promise>}
+ * @example
+ * ```js
+ * Multiaddr.resolvers.set('dnsaddr', resolverFunction)
+ * const mh1 = new Multiaddr('/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb')
+ * const resolvedMultiaddrs = await mh1.resolve()
+ * // [
+ * // ,
+ * // ,
+ * //
+ * // ]
+ * ```
+ */
+ async resolve () {
+ const resolvableProto = this.protos().find((p) => p.resolvable)
-/**
- * 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: number}}
- * @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 () {
- const codes = this.protoCodes()
- const names = this.protoNames()
- const parts = this.toString().split('/').slice(1)
-
- if (parts.length < 4) {
- throw new Error('multiaddr must have a valid format: "/{ip4, ip6, dns4, dns6}/{address}/{tcp, udp}/{port}".')
- } else if (codes[0] !== 4 && codes[0] !== 41 && codes[0] !== 54 && codes[0] !== 55) {
- throw new Error(`no protocol with name: "'${names[0]}'". Must have a valid family name: "{ip4, ip6, dns4, dns6}".`)
- } else if (parts[2] !== 'tcp' && parts[2] !== 'udp') {
- throw new Error(`no protocol with name: "'${names[1]}'". Must have a valid transport protocol: "{tcp, udp}".`)
- }
+ // Multiaddr is not resolvable?
+ if (!resolvableProto) {
+ return [this]
+ }
- return {
- family: (codes[0] === 41 || codes[0] === 55) ? 6 : 4,
- address: parts[1], // ip addr
- port: parseInt(parts[3]) // tcp or udp port
- }
-}
+ const resolver = resolvers.get(resolvableProto.name)
+ if (!resolver) {
+ throw errCode(new Error(`no available resolver for ${resolvableProto.name}`), 'ERR_NO_AVAILABLE_RESOLVER')
+ }
-/**
- * Creates a Multiaddr from a node-friendly address object
- *
- * @param {{family: string, address: string, port: number}} 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')
- let ip
- switch (addr.family) {
- case 'IPv4':
- ip = 'ip4'
- break
- case 'IPv6':
- ip = 'ip6'
- break
- default:
- throw Error(`Invalid addr family. Got '${addr.family}' instead of 'IPv4' or 'IPv6'`)
+ const addresses = await resolver(this)
+ return addresses.map(a => new Multiaddr(a))
}
- return Multiaddr('/' + [ip, addr.address, transport, addr.port].join('/'))
-}
-// 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) {
- const protos = (addr || this).protos()
+ /**
+ * 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: number, address: string, port: number}}
+ * @throws {Error} Throws error if Multiaddr is not a Thin Waist address
+ * @example
+ *
+ * ```js
+ * new Multiaddr('/ip4/127.0.0.1/tcp/4001').nodeAddress()
+ * // {family: 4, address: '127.0.0.1', port: '4001'}
+ * ```
+ *
+ */
+ nodeAddress () {
+ const codes = this.protoCodes()
+ const names = this.protoNames()
+ const parts = this.toString().split('/').slice(1)
+
+ if (parts.length < 4) {
+ throw new Error('multiaddr must have a valid format: "/{ip4, ip6, dns4, dns6}/{address}/{tcp, udp}/{port}".')
+ } else if (codes[0] !== 4 && codes[0] !== 41 && codes[0] !== 54 && codes[0] !== 55) {
+ throw new Error(`no protocol with name: "'${names[0]}'". Must have a valid family name: "{ip4, ip6, dns4, dns6}".`)
+ } else if (parts[2] !== 'tcp' && parts[2] !== 'udp') {
+ throw new Error(`no protocol with name: "'${names[1]}'". Must have a valid transport protocol: "{tcp, udp}".`)
+ }
- if (protos.length !== 2) {
- return false
+ return {
+ family: (codes[0] === 41 || codes[0] === 55) ? 6 : 4,
+ address: parts[1],
+ port: parseInt(parts[3]) // tcp or udp port
+ }
}
- if (protos[0].code !== 4 && protos[0].code !== 41) {
- return false
+ /**
+ * 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
+ * ```js
+ * const mh1 = new Multiaddr('/ip4/127.0.0.1/tcp/4001')
+ * //
+ * const mh2 = new Multiaddr('/ip4/192.168.2.1/tcp/5001')
+ * //
+ * const mh3 = mh1.encapsulate(mh2)
+ * //
+ * const mh4 = new Multiaddr('/ip4/127.0.0.1/tcp/2000/wss/p2p-webrtc-star/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooo2a')
+ * //
+ * mh1.isThinWaistAddress()
+ * // true
+ * mh2.isThinWaistAddress()
+ * // true
+ * mh3.isThinWaistAddress()
+ * // false
+ * mh4.isThinWaistAddress()
+ * // false
+ * ```
+ */
+ isThinWaistAddress (addr) {
+ const protos = (addr || this).protos()
+
+ if (protos.length !== 2) {
+ return false
+ }
+
+ if (protos[0].code !== 4 && protos[0].code !== 41) {
+ return false
+ }
+ if (protos[1].code !== 6 && protos[1].code !== 273) {
+ return false
+ }
+ return true
}
- if (protos[1].code !== 6 && protos[1].code !== 273) {
- return false
+
+ /**
+ * Creates a Multiaddr from a node-friendly address object
+ *
+ * @param {{family: string, address: string, port: string}} addr
+ * @param {string} transport
+ * @returns {Multiaddr} multiaddr
+ * @throws {Error} Throws error if addr or transport are not truthy
+ * @example
+ *
+ * ```js
+ * Multiaddr.fromNodeAddress({address: '127.0.0.1', port: '4001'}, 'tcp')
+ * //
+ * ```
+ */
+ static fromNodeAddress (addr, transport) {
+ if (!addr) { throw new Error('requires node address object') }
+ if (!transport) { throw new Error('requires transport protocol') }
+ let ip
+ switch (addr.family) {
+ case 'IPv4':
+ ip = 'ip4'
+ break
+ case 'IPv6':
+ ip = 'ip6'
+ break
+ default:
+ throw Error(`Invalid addr family. Got '${addr.family}' instead of 'IPv4' or 'IPv6'`)
+ }
+ return new Multiaddr('/' + [ip, addr.address, transport, addr.port].join('/'))
}
- return true
-}
-/**
- * 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 that is a name
+ *
+ * @param {Multiaddr} addr
+ * @returns {boolean} isName
+ */
+ static isName (addr) {
+ if (!Multiaddr.isMultiaddr(addr)) {
+ return false
+ }
-/**
- * Returns if something is a Multiaddr that is a name
- *
- * @param {Multiaddr} addr
- * @returns {Bool} isName
- */
-Multiaddr.isName = function isName (addr) {
- if (!Multiaddr.isMultiaddr(addr)) {
- return false
+ // if a part of the multiaddr is resolvable, then return true
+ return addr.protos().some((proto) => proto.resolvable)
}
- // if a part of the multiaddr is resolvable, then return true
- return addr.protos().some((proto) => proto.resolvable)
-}
-
-/**
- * Returns an array of multiaddrs, by resolving the multiaddr that is a name
- *
- * @async
- * @param {Multiaddr} addr
- * @returns {Multiaddr[]}
- */
-Multiaddr.resolve = function resolve (addr) {
- if (!Multiaddr.isMultiaddr(addr) || !Multiaddr.isName(addr)) {
- return Promise.reject(Error('not a valid name'))
+ /**
+ * Check if object is a CID instance
+ *
+ * @param {any} value
+ * @returns {value is Multiaddr}
+ */
+ static isMultiaddr (value) {
+ return value instanceof Multiaddr || Boolean(value && value[symbol])
}
- /*
- * Needs more consideration from spec design:
- * - what to return
- * - how to achieve it in the browser?
+ /**
+ * Static factory method
+ *
+ * @param {string | Uint8Array | Multiaddr | null} [addr] - If String or Uint8Array, needs to adhere
+ * to the address format of a [multiaddr](https://github.com/multiformats/multiaddr#string-format)
*/
- return Promise.reject(new Error('not implemented yet'))
+ static multiaddr (addr) {
+ return new Multiaddr(addr)
+ }
}
+Multiaddr.protocols = protocols
+
Multiaddr.resolvers = resolvers
-exports = module.exports = Multiaddr
+
+module.exports = Multiaddr
diff --git a/src/protocols-table.js b/src/protocols-table.js
index 4578e436..36d4b92e 100644
--- a/src/protocols-table.js
+++ b/src/protocols-table.js
@@ -1,5 +1,12 @@
'use strict'
+/** @typedef {import("./types").Protocol} Protocol */
+/**
+ * Protocols
+ *
+ * @param {number | string} proto
+ * @returns {Protocol}
+ */
function Protocols (proto) {
if (typeof (proto) === 'number') {
if (Protocols.codes[proto]) {
@@ -7,7 +14,7 @@ function Protocols (proto) {
}
throw new Error('no protocol with code: ' + proto)
- } else if (typeof (proto) === 'string' || proto instanceof String) {
+ } else if (typeof (proto) === 'string') {
if (Protocols.names[proto]) {
return Protocols.names[proto]
}
@@ -22,6 +29,7 @@ const V = -1
Protocols.lengthPrefixedVarSize = V
Protocols.V = V
+/** @type {Array<[number, number, string, (string|boolean)?, string?]>} */
Protocols.table = [
[4, 32, 'ip4'],
[6, 16, 'tcp'],
@@ -59,7 +67,9 @@ Protocols.table = [
[777, V, 'memory']
]
+/** @type {Record} */
Protocols.names = {}
+/** @type {Record} */
Protocols.codes = {}
// populate tables
@@ -71,6 +81,17 @@ Protocols.table.map(row => {
Protocols.object = p
+/**
+ *
+ * Create a protocol
+ *
+ * @param {number} code
+ * @param {number} size
+ * @param {string} name
+ * @param {any} [resolvable]
+ * @param {any} [path]
+ * @returns {Protocol}
+ */
function p (code, size, name, resolvable, path) {
return {
code,
diff --git a/src/resolvers/dns.js b/src/resolvers/dns.js
index e85a5bd2..b54cfb29 100644
--- a/src/resolvers/dns.js
+++ b/src/resolvers/dns.js
@@ -1,9 +1,10 @@
'use strict'
+/** @type {typeof import('dns').promises.Resolver} */
let dns
try {
- dns = require('dns').promises
+ dns = require('dns').promises.Resolver
if (!dns) {
throw new Error('no dns available')
}
diff --git a/src/resolvers/index.js b/src/resolvers/index.js
index d14c5c99..5c224b73 100644
--- a/src/resolvers/index.js
+++ b/src/resolvers/index.js
@@ -1,25 +1,24 @@
'use strict'
-const Multiaddr = require('..') // eslint-disable-line no-unused-vars
const protocols = require('../protocols-table')
const { code: dnsaddrCode } = protocols('dnsaddr')
+// TODO `addr` type needs https://github.com/microsoft/TypeScript/issues/41672
/**
* Resolver for dnsaddr addresses.
*
- * @param {Multiaddr} addr
+ * @param {any} addr
* @returns {Promise>}
*/
async function dnsaddrResolver (addr) {
- const { Resolver } = require('./dns')
+ const Resolver = require('./dns')
const resolver = new Resolver()
const peerId = addr.getPeerId()
const [, hostname] = addr.stringTuples().find(([proto]) => proto === dnsaddrCode) || []
const records = await resolver.resolveTxt(`_dnsaddr.${hostname}`)
- // @ts-ignore
let addresses = records.flat().map((a) => a.split('=')[1])
if (peerId) {
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 00000000..9ae0bef5
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,7 @@
+export type Protocol = {
+ code: number;
+ size: number;
+ name: string;
+ resolvable?: boolean | undefined;
+ path?: boolean | undefined;
+}
diff --git a/test/index.spec.js b/test/index.spec.js
index c154eb4a..aa0aeccc 100644
--- a/test/index.spec.js
+++ b/test/index.spec.js
@@ -2,16 +2,18 @@
/* eslint-env mocha */
'use strict'
-const multiaddr = require('../src')
+const Multiaddr = require('../src')
const { expect } = require('aegir/utils/chai')
const uint8ArrayFromString = require('uint8arrays/from-string')
+const multiaddr = Multiaddr.multiaddr
+
describe('construction', () => {
let udpAddr
it('create multiaddr', () => {
udpAddr = multiaddr('/ip4/127.0.0.1/udp/1234')
- expect(udpAddr instanceof multiaddr).to.equal(true)
+ expect(udpAddr instanceof Multiaddr).to.equal(true)
})
it('clone multiaddr', () => {
@@ -77,7 +79,7 @@ describe('requiring varint', () => {
it('create multiaddr', () => {
uTPAddr = multiaddr('/ip4/127.0.0.1/udp/1234/utp')
- expect(uTPAddr instanceof multiaddr).to.equal(true)
+ expect(uTPAddr instanceof Multiaddr).to.equal(true)
})
it('clone multiaddr', () => {
@@ -116,16 +118,16 @@ describe('manipulation', () => {
expect(udpAddr.protoCodes()).to.deep.equal([4, 273])
expect(udpAddr.protoNames()).to.deep.equal(['ip4', 'udp'])
- expect(udpAddr.protos()).to.deep.equal([multiaddr.protocols.codes[4], multiaddr.protocols.codes[273]])
- expect(udpAddr.protos()[0] === multiaddr.protocols.codes[4]).to.equal(false)
+ expect(udpAddr.protos()).to.deep.equal([Multiaddr.protocols.codes[4], Multiaddr.protocols.codes[273]])
+ expect(udpAddr.protos()[0] === Multiaddr.protocols.codes[4]).to.equal(false)
- const udpAddrbytes2 = udpAddr.encapsulate('/udp/5678')
+ const udpAddrbytes2 = udpAddr.encapsulate(multiaddr('/udp/5678'))
expect(udpAddrbytes2.toString()).to.equal('/ip4/127.0.0.1/udp/1234/udp/5678')
- expect(udpAddrbytes2.decapsulate('/udp').toString()).to.equal('/ip4/127.0.0.1/udp/1234')
- expect(udpAddrbytes2.decapsulate('/ip4').toString()).to.equal('/')
- expect(function () { udpAddr.decapsulate('/').toString() }).to.throw()
+ expect(udpAddrbytes2.decapsulate(multiaddr('/udp/5678')).toString()).to.equal('/ip4/127.0.0.1/udp/1234')
+ expect(udpAddrbytes2.decapsulate(multiaddr('/ip4/127.0.0.1')).toString()).to.equal('/')
+ expect(function () { udpAddr.decapsulate(multiaddr('/')).toString() }).to.throw()
expect(multiaddr('/').encapsulate(udpAddr).toString()).to.equal(udpAddr.toString())
- expect(multiaddr('/').decapsulate('/').toString()).to.equal('/')
+ expect(multiaddr('/').decapsulate(multiaddr('/')).toString()).to.equal('/')
})
it('ipfs', () => {
@@ -628,7 +630,7 @@ describe('helpers', () => {
describe('.decapsulate', () => {
it('throws on address with no matching subaddress', () => {
expect(
- () => multiaddr('/ip4/127.0.0.1').decapsulate('/ip4/198.168.0.0')
+ () => multiaddr('/ip4/127.0.0.1').decapsulate(multiaddr('/ip4/198.168.0.0'))
).to.throw(
/does not contain subaddress/
)
@@ -638,7 +640,7 @@ describe('helpers', () => {
describe('.decapsulateCode', () => {
it('removes the last occurrence of the code from the multiaddr', () => {
const relayTCP = multiaddr('/ip4/0.0.0.0/tcp/8080')
- const relay = relayTCP.encapsulate('/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6/p2p-circuit')
+ const relay = relayTCP.encapsulate(multiaddr('/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6/p2p-circuit'))
const target = multiaddr('/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC')
const original = relay.encapsulate(target)
expect(original.decapsulateCode(421)).to.eql(relay)
@@ -725,7 +727,7 @@ describe('helpers', () => {
it('throws on missing address object', () => {
expect(
// @ts-ignore
- () => multiaddr.fromNodeAddress()
+ () => Multiaddr.fromNodeAddress()
).to.throw(
/requires node address/
)
@@ -734,7 +736,7 @@ describe('helpers', () => {
it('throws on missing transport', () => {
expect(
// @ts-ignore
- () => multiaddr.fromNodeAddress({ address: '0.0.0.0' })
+ () => Multiaddr.fromNodeAddress({ address: '0.0.0.0' })
).to.throw(
/requires transport protocol/
)
@@ -742,7 +744,7 @@ describe('helpers', () => {
it('parses a node address', () => {
expect(
- multiaddr.fromNodeAddress({
+ Multiaddr.fromNodeAddress({
address: '192.168.0.1',
family: 'IPv4',
port: '1234'
@@ -857,11 +859,11 @@ describe('helpers', () => {
describe('multiaddr.isMultiaddr', () => {
it('handles different inputs', () => {
- expect(multiaddr.isMultiaddr(multiaddr('/'))).to.be.eql(true)
- expect(multiaddr.isMultiaddr('/')).to.be.eql(false)
- expect(multiaddr.isMultiaddr(123)).to.be.eql(false)
+ expect(Multiaddr.isMultiaddr(multiaddr('/'))).to.be.eql(true)
+ expect(Multiaddr.isMultiaddr('/')).to.be.eql(false)
+ expect(Multiaddr.isMultiaddr(123)).to.be.eql(false)
- expect(multiaddr.isMultiaddr(uint8ArrayFromString('/hello'))).to.be.eql(false)
+ expect(Multiaddr.isMultiaddr(uint8ArrayFromString('/hello'))).to.be.eql(false)
})
})
@@ -870,47 +872,31 @@ describe('helpers', () => {
it('valid name dns', () => {
const str = '/dns/ipfs.io'
const addr = multiaddr(str)
- expect(multiaddr.isName(addr)).to.equal(true)
+ expect(Multiaddr.isName(addr)).to.equal(true)
})
it('valid name dnsaddr', () => {
const str = '/dnsaddr/ipfs.io'
const addr = multiaddr(str)
- expect(multiaddr.isName(addr)).to.equal(true)
+ expect(Multiaddr.isName(addr)).to.equal(true)
})
it('valid name dns4', () => {
const str = '/dns4/ipfs.io'
const addr = multiaddr(str)
- expect(multiaddr.isName(addr)).to.equal(true)
+ expect(Multiaddr.isName(addr)).to.equal(true)
})
it('valid name dns6', () => {
const str = '/dns6/ipfs.io'
const addr = multiaddr(str)
- expect(multiaddr.isName(addr)).to.equal(true)
+ expect(Multiaddr.isName(addr)).to.equal(true)
})
it('invalid name', () => {
const str = '/ip4/127.0.0.1'
const addr = multiaddr(str)
- expect(multiaddr.isName(addr)).to.equal(false)
- })
- })
-
- describe('.resolve', () => {
- it.skip('valid and active DNS name', (done) => {})
- it.skip('valid but inactive DNS name', () => {})
- it('invalid DNS name', () => {
- const str = '/ip4/127.0.0.1'
- const addr = multiaddr(str)
-
- return multiaddr.resolve(addr)
- // @ts-ignore
- .then(expect.fail, (err) => {
- expect(err).to.exist()
- expect(err.message).to.eql('not a valid name')
- })
+ expect(Multiaddr.isName(addr)).to.equal(false)
})
})
})
diff --git a/test/protocols.spec.js b/test/protocols.spec.js
index b5b53016..a6e12a01 100644
--- a/test/protocols.spec.js
+++ b/test/protocols.spec.js
@@ -24,6 +24,7 @@ describe('protocols', () => {
it('else', () => {
expect(
+ // @ts-expect-error
() => protocols({ hi: 34 })
).to.throw(
/invalid protocol id type/
diff --git a/test/resolvers.spec.js b/test/resolvers.spec.js
index 747c80de..5ac692e6 100644
--- a/test/resolvers.spec.js
+++ b/test/resolvers.spec.js
@@ -4,9 +4,10 @@
const { expect } = require('aegir/utils/chai')
const sinon = require('sinon')
-const multiaddr = require('../src')
+const Multiaddr = require('../src')
const resolvers = require('../src/resolvers')
-const { Resolver } = require('../src/resolvers/dns')
+const Resolver = require('../src/resolvers/dns')
+const multiaddr = Multiaddr.multiaddr
const dnsaddrStub1 = [
['dnsaddr=/dnsaddr/ams-1.bootstrap.libp2p.io/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd'],
@@ -28,7 +29,7 @@ const dnsaddrStub2 = [
describe('multiaddr resolve', () => {
it('should throw if no resolver is available', async () => {
- const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io')
+ const ma = new Multiaddr('/dnsaddr/bootstrap.libp2p.io')
// Resolve
await expect(ma.resolve()).to.eventually.be.rejected()
@@ -38,7 +39,7 @@ describe('multiaddr resolve', () => {
describe('dnsaddr', () => {
before(() => {
// Set resolvers
- multiaddr.resolvers.set('dnsaddr', resolvers.dnsaddrResolver)
+ Multiaddr.resolvers.set('dnsaddr', resolvers.dnsaddrResolver)
})
afterEach(() => {
diff --git a/test/types.spec.ts b/test/types.spec.ts
deleted file mode 100644
index ba9129c9..00000000
--- a/test/types.spec.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import Multiaddr from '../src'
-
-const testStr = '/ip4/127.0.0.1'
-
-export const maFromFunctionConstructor: Multiaddr = Multiaddr(testStr)
-
-export const maFromClassConstructor: Multiaddr = new Multiaddr(testStr)
-
-export const maFromMa: Multiaddr = Multiaddr(new Multiaddr(testStr))
-
-export const maFromConstructorFunction: Multiaddr = Multiaddr.fromNodeAddress(
- {
- family: 'IPv4',
- address: '127.0.0.1',
- port: '12345'
- },
- 'udp'
-)
-
-export function doSthWithMa (ma: Multiaddr): void {
- ma.toOptions()
-}
diff --git a/tsconfig.json b/tsconfig.json
index 60e2c5b1..e605b61a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,18 +1,10 @@
{
- "compilerOptions": {
- "module": "commonjs",
- "lib": ["es6"],
- "target": "ES5",
- "noImplicitAny": false,
- "noImplicitThis": true,
- "strictFunctionTypes": true,
- "strictNullChecks": true,
- "esModuleInterop": true,
- "resolveJsonModule": true,
- "allowJs": true,
- "checkJs": true,
- "noEmit": true,
- "forceConsistentCasingInFileNames": true
- },
- "include": ["src/index.d.ts", "test/**/*.spec.js", "test/**/*.spec.ts"]
+ "extends": "./node_modules/aegir/src/config/tsconfig.aegir.json",
+ "compilerOptions": {
+ "outDir": "dist"
+ },
+ "include": [
+ "test", // remove this line if you don't want to type-check tests
+ "src"
+ ]
}