From 6cc856edd6cd5bb645980e91edae5ad1cb5a3986 Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Sat, 30 Jun 2018 23:24:35 +0200
Subject: [PATCH 1/7] chore: add Jenkins and maintainer

---
 README.md      |  9 ++++++---
 ci/Jenkinsfile |  1 +
 package.json   | 11 ++++++-----
 3 files changed, 13 insertions(+), 8 deletions(-)
 create mode 100644 ci/Jenkinsfile

diff --git a/README.md b/README.md
index c06fce5..4144d97 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,14 @@
 is-ipfs
 ====
 
-[![build status](https://secure.travis-ci.org/ipfs/is-ipfs.svg)](http://travis-ci.org/ipfs/is-ipfs)
-[![dignified.js](https://img.shields.io/badge/follows-dignified.js-blue.svg?style=flat-square)](https://github.com/dignifiedquire/dignified.js)
+[![](https://img.shields.io/github/release/ipfs/ipfs-companion.svg)](https://github.com/ipfs/ipfs-companion/releases/latest)
+[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
 
-A set of utilities to help identify [IPFS](https://ipfs.io/) resources.
+> A set of utilities to help identify [IPFS](https://ipfs.io/) resources
 
+## Lead Maintainer
+
+[Marcin Rataj](https://github.com/lidel)
 
 ## Install
 
diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile
new file mode 100644
index 0000000..c1e3f2c
--- /dev/null
+++ b/ci/Jenkinsfile
@@ -0,0 +1 @@
+javascript()
diff --git a/package.json b/package.json
index 2dbcceb..54bf002 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "is-ipfs",
-  "version": "0.3.2",
+  "version": "0.3.3",
   "description": "A set of utilities to help identify IPFS resources",
   "main": "src/index.js",
   "browser": {
@@ -23,6 +23,7 @@
     "lint"
   ],
   "keywords": [
+    "js-ipfs",
     "ipfs"
   ],
   "author": "Francisco Dias <francisco@baiodias.com> (http://franciscodias.net/)",
@@ -39,16 +40,16 @@
   },
   "repository": {
     "type": "git",
-    "url": "https://github.com/xicombd/is-ipfs.git"
+    "url": "https://github.com/ipfs/is-ipfs.git"
   },
   "bugs": {
-    "url": "https://github.com/xicombd/is-ipfs/issues"
+    "url": "https://github.com/ipfs/is-ipfs/issues"
   },
-  "homepage": "https://github.com/xicombd/is-ipfs",
+  "homepage": "https://github.com/ipfs/is-ipfs",
   "contributors": [
     "David Dias <daviddias.p@gmail.com>",
     "Francisco Baio Dias <xicombd@gmail.com>",
     "Marcin Rataj <lidel@lidel.org>",
     "nginnever <ginneversource@gmail.com>"
   ]
-}
\ No newline at end of file
+}

From ae0f73809dc55989b5465626c30bdd9ad147e4ef Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Sun, 1 Jul 2018 18:50:45 +0200
Subject: [PATCH 2/7] fix: release badge in readme

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4144d97..0b2b710 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 is-ipfs
 ====
 
-[![](https://img.shields.io/github/release/ipfs/ipfs-companion.svg)](https://github.com/ipfs/ipfs-companion/releases/latest)
+[![](https://img.shields.io/github/release/ipfs/is-ipfs.svg)](https://github.com/ipfs/is-ipfs/releases/latest)
 [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
 
 > A set of utilities to help identify [IPFS](https://ipfs.io/) resources

From a793da796d2905d36d6c1041b894c471db36695f Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Sun, 22 Jul 2018 23:38:47 +0200
Subject: [PATCH 3/7] feat: support cidv1b32 in subdomains

Closes https://github.com/ipfs/is-ipfs/issues/19
---
 .gitignore                  |   1 +
 README.md                   |  81 +++++++++++++++++---
 package.json                |  35 ++++-----
 src/index.js                |  48 ++++++++++--
 test/test-cid.spec.js       |   9 +++
 test/test-multibase.spec.js |  73 ++++++++++++++++++
 test/test-subdomain.spec.js | 146 ++++++++++++++++++++++++++++++++++++
 7 files changed, 359 insertions(+), 34 deletions(-)
 create mode 100644 test/test-multibase.spec.js
 create mode 100644 test/test-subdomain.spec.js

diff --git a/.gitignore b/.gitignore
index 578a0d0..858990e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ lib-cov
 
 # Coverage directory used by tools like istanbul
 coverage
+.nyc_output
 
 # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
 .grunt
diff --git a/README.md b/README.md
index 0b2b710..372be34 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ is-ipfs
 
 [Marcin Rataj](https://github.com/lidel)
 
-## Install
+# Install
 
 ### In Node.js through npm
 
@@ -37,17 +37,25 @@ Loading this module through a script tag will make the ```IsIpfs``` obj availabl
 <script src="https://unpkg.com/is-ipfs/dist/index.js"></script>
 ```
 
-## Usage
+# Usage
 ```javascript
 const isIPFS = require('is-ipfs')
 
 isIPFS.multihash('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true
 isIPFS.multihash('noop') // false
 
+isIPFS.multibase('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') // 'base32'
+isIPFS.multibase('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7') // 'base58btc'
+isIPFS.multibase('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false (no multibase prefix in CIDv0)
+isIPFS.multibase('noop') // false
+
 isIPFS.cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true (CIDv0)
 isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7') // true (CIDv1)
 isIPFS.cid('noop') // false
 
+isIPFS.base32cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') // true
+isIPFS.base32cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false
+
 isIPFS.url('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true
 isIPFS.url('https://ipfs.io/ipns/github.com') // true
 isIPFS.url('https://github.com/ipfs/js-ipfs/blob/master/README.md') // false
@@ -74,29 +82,52 @@ isIPFS.ipfsPath('/ipfs/invalid-hash') // false
 
 isIPFS.ipnsPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false
 isIPFS.ipnsPath('/ipns/github.com') // true
+
+isIPFS.subdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') // true
+isIPFS.subdomain('http://www.bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // false
+isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') // false
+
+isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true
+isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') // false
+
+isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') // true
+isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.dweb.link') // false
+isIPFS.ipnsSubdomain('http://QmcNioXSC1bfJj1dcFErhUfyjFzoX2HodkRccsFFVJJvg8.ipns.dweb.link') // false
+isIPFS.ipnsSubdomain('http://foo-bar.ipns.dweb.link') // false (not a PeerID)
 ```
 
-## API
+# API
+
+A suite of util methods provides efficient validation.
+
+Detection of IPFS Paths and identifiers in URLs is a two-stage process:
+1.  `urlPattern`/`pathPattern`/`subdomainPattern` regex is applied to quickly identify potential candidates
+2.  proper CID validation is applied to remove false-positives
+
+
+## Utils
 
 ### `isIPFS.multihash(hash)`
 
 Returns `true` if the provided string is a valid `multihash` or `false` otherwise.
 
+### `isIPFS.multibase(cid)`
+
+Returns a string with multibase name if the provided CID has `multibase` prefix or `false` otherwise.
+
 ### `isIPFS.cid(hash)`
 
 Returns `true` if the provided string is a valid `CID` or `false` otherwise.
 
-### `isIPFS.url(url)`
+### `isIPFS.base32cid(hash)`
 
-Returns `true` if the provided string is a valid IPFS or IPNS url or `false` otherwise.
+Returns `true` if the provided string is a valid `CID` in Base32 encoding or `false` otherwise.
 
-### `isIPFS.path(path)`
+## URLs
 
-Returns `true` if the provided string is a valid IPFS or IPNS path or `false` otherwise.
-
-### `isIPFS.urlOrPath(path)`
+### `isIPFS.url(url)`
 
-Returns `true` if the provided string is a valid IPFS or IPNS url or path or `false` otherwise.
+Returns `true` if the provided string is a valid IPFS or IPNS url or `false` otherwise.
 
 ### `isIPFS.ipfsUrl(url)`
 
@@ -106,6 +137,19 @@ Returns `true` if the provided string is a valid IPFS url or `false` otherwise.
 
 Returns `true` if the provided string is a valid IPNS url or `false` otherwise.
 
+## Paths
+
+Standalone validation of IPFS Paths: `/ip(f|n)s/<cid>/..`
+
+### `isIPFS.path(path)`
+
+Returns `true` if the provided string is a valid IPFS or IPNS path or `false` otherwise.
+
+### `isIPFS.urlOrPath(path)`
+
+Returns `true` if the provided string is a valid IPFS or IPNS url or path or `false` otherwise.
+
+
 ### `isIPFS.ipfsPath(path)`
 
 Returns `true` if the provided string is a valid IPFS path or `false` otherwise.
@@ -114,10 +158,23 @@ Returns `true` if the provided string is a valid IPFS path or `false` otherwise.
 
 Returns `true` if the provided string is a valid IPNS path or `false` otherwise.
 
+## Subdomains
+
+Validated subdomain convention: `cidv1b32.ip(f|n)s.domain.tld`
+
+### `isIPFS.subdomain(url)`
+
+Returns `true` if the provided string includes a valid IPFS or IPNS subdomain or `false` otherwise.
+
+### `isIPFS.ipfsSubdomain(url)`
+
+Returns `true` if the provided string includes a valid IPFS subdomain or `false` otherwise.
+
+### `isIPFS.ipnsSubdomain(url)`
 
-**Note:** the regex used for these checks is also exported as `isIPFS.urlPattern`
+Returns `true` if the provided string includes a valid IPNS subdomain or `false` otherwise.
 
-## License
 
+# License
 
 MIT
diff --git a/package.json b/package.json
index 54bf002..5c3cc26 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,22 @@
 {
   "name": "is-ipfs",
-  "version": "0.3.3",
+  "version": "0.4.0",
   "description": "A set of utilities to help identify IPFS resources",
   "main": "src/index.js",
   "browser": {
     "fs": false
   },
   "scripts": {
-    "test:node": "aegir-test node",
-    "test:browser": "aegir-test browser",
-    "test": "aegir-test",
-    "lint": "aegir-lint",
-    "release": "aegir-release",
-    "release-minor": "aegir-release --type minor",
-    "release-major": "aegir-release --type major",
-    "build": "aegir-build",
-    "coverage": "aegir-coverage",
-    "coverage-publish": "aegir-coverage publish"
+    "test:node": "aegir test --target node",
+    "test:browser": "aegir test --target browser",
+    "test": "aegir test",
+    "lint": "aegir lint",
+    "release": "aegir release",
+    "release-minor": "aegir release --type minor",
+    "release-major": "aegir release --type major",
+    "build": "aegir build",
+    "coverage": "aegir coverage",
+    "coverage-publish": "aegir coverage --upload"
   },
   "pre-commit": [
     "test",
@@ -29,14 +29,15 @@
   "author": "Francisco Dias <francisco@baiodias.com> (http://franciscodias.net/)",
   "license": "MIT",
   "dependencies": {
-    "cids": "~0.5.1",
-    "bs58": "^4.0.1",
-    "multihashes": "~0.4.9"
+    "bs58": "4.0.1",
+    "cids": "0.5.3",
+    "multibase": "0.4.0",
+    "multihashes": "0.4.13"
   },
   "devDependencies": {
-    "aegir": "^11.0.2",
-    "chai": "^4.1.2",
-    "pre-commit": "^1.2.2"
+    "aegir": "15.0.1",
+    "chai": "4.1.2",
+    "pre-commit": "1.2.2"
   },
   "repository": {
     "type": "git",
diff --git a/src/index.js b/src/index.js
index 516b2db..be394e7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,10 +2,17 @@
 
 const base58 = require('bs58')
 const multihash = require('multihashes')
+const multibase = require('multibase')
 const CID = require('cids')
 
 const urlPattern = /^https?:\/\/[^/]+\/(ip(f|n)s)\/((\w+).*)/
 const pathPattern = /^\/(ip(f|n)s)\/((\w+).*)/
+const defaultProtocolMatch = 1
+const defaultHashMath = 4
+
+const fqdnPattern = /^https?:\/\/([^/]+)\.(ip(?:f|n)s)\.[^/]+/
+const fqdnHashMatch = 1
+const fqdnProtocolMatch = 2
 
 function isMultihash (hash) {
   const formatted = convertToString(hash)
@@ -18,6 +25,14 @@ function isMultihash (hash) {
   }
 }
 
+function isMultibase (hash) {
+  try {
+    return multibase.isEncoded(hash)
+  } catch (e) {
+    return false
+  }
+}
+
 function isCID (hash) {
   try {
     return CID.isCID(new CID(hash))
@@ -26,7 +41,7 @@ function isCID (hash) {
   }
 }
 
-function isIpfs (input, pattern) {
+function isIpfs (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch = defaultHashMath) {
   const formatted = convertToString(input)
   if (!formatted) {
     return false
@@ -37,15 +52,23 @@ function isIpfs (input, pattern) {
     return false
   }
 
-  if (match[1] !== 'ipfs') {
+  if (match[protocolMatch] !== 'ipfs') {
     return false
   }
 
-  const hash = match[4]
+  let hash = match[hashMatch]
+
+  if (hash && pattern === fqdnPattern) {
+    // when doing checks for subdomain context
+    // ensure hash is case-insensitive
+    // (browsers force-lowercase authority compotent anyway)
+    hash = hash.toLowerCase()
+  }
+
   return isCID(hash)
 }
 
-function isIpns (input, pattern) {
+function isIpns (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch) {
   const formatted = convertToString(input)
   if (!formatted) {
     return false
@@ -55,10 +78,19 @@ function isIpns (input, pattern) {
     return false
   }
 
-  if (match[1] !== 'ipns') {
+  if (match[protocolMatch] !== 'ipns') {
     return false
   }
 
+  if (hashMatch && pattern === fqdnPattern) {
+    let hash = match[hashMatch]
+    // when doing checks for subdomain context
+    // ensure hash is case-insensitive
+    // (browsers force-lowercase authority compotent anyway)
+    hash = hash.toLowerCase()
+    return isCID(hash)
+  }
+
   return true
 }
 
@@ -76,7 +108,13 @@ function convertToString (input) {
 
 module.exports = {
   multihash: isMultihash,
+  multibase: isMultibase,
   cid: isCID,
+  base32cid: (cid) => (isMultibase(cid) === 'base32' && isCID(cid)),
+  ipfsSubdomain: (url) => isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch),
+  ipnsSubdomain: (url) => isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch),
+  subdomain: (url) => (isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch) || isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch)),
+  subdomainPattern: fqdnPattern,
   ipfsUrl: (url) => isIpfs(url, urlPattern),
   ipnsUrl: (url) => isIpns(url, urlPattern),
   url: (url) => (isIpfs(url, urlPattern) || isIpns(url, urlPattern)),
diff --git a/test/test-cid.spec.js b/test/test-cid.spec.js
index 94f66e6..8237d07 100644
--- a/test/test-cid.spec.js
+++ b/test/test-cid.spec.js
@@ -36,12 +36,19 @@ describe('ipfs cid', () => {
     done()
   })
 
+  it('isIPFS.cid should match a valid CIDv1 in Base32', (done) => {
+    const actual = isIPFS.cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal(true)
+    done()
+  })
+
   it('isIPFS.cid should not match an invalid CIDv1 (with a typo)', (done) => {
     const actual = isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ')
     expect(actual).to.equal(false)
     done()
   })
 
+
   it('isIPFS.cid should not match an invalid CID', (done) => {
     const actual = isIPFS.cid('noop')
     expect(actual).to.equal(false)
@@ -53,4 +60,6 @@ describe('ipfs cid', () => {
     expect(actual).to.equal(false)
     done()
   })
+
+
 })
diff --git a/test/test-multibase.spec.js b/test/test-multibase.spec.js
new file mode 100644
index 0000000..bab9b3f
--- /dev/null
+++ b/test/test-multibase.spec.js
@@ -0,0 +1,73 @@
+/* eslint-env mocha */
+'use strict'
+
+const base58btc = require('bs58')
+const expect = require('chai').expect
+const isIPFS = require('../src/index')
+
+describe('ipfs base32cid', () => {
+  it('isIPFS.base32cid should not match a valid CIDv0 (multihash in base58btc)', (done) => {
+    const actual = isIPFS.base32cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match a valid CIDv1 in base58btc', (done) => {
+    const actual = isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.base32cid should match a valid URL-safe CIDv1 in Base32', (done) => {
+    const actual = isIPFS.base32cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match an invalid CID (with a typo)', (done) => {
+    const actual = isIPFS.base32cid('afybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+
+  it('isIPFS.base32cid should not match an invalid CID', (done) => {
+    const actual = isIPFS.base32cid('noop')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match an invalid CID data type', (done) => {
+    const actual = isIPFS.base32cid(4)
+    expect(actual).to.equal(false)
+    done()
+  })
+
+})
+
+describe('ipfs multibase', () => {
+  it('isIPFS.multibase should return false for a valid CIDv0 (raw multihash in base58btc, nor multibase prefix)', (done) => {
+    const actual = isIPFS.multibase('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.multibase should match a valid CIDv1 in base58btc', (done) => {
+    const actual = isIPFS.multibase('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7')
+    expect(actual).to.equal('base58btc')
+    done()
+  })
+
+  it('isIPFS.multibase should match CIDv1 in Base32', (done) => {
+    const actual = isIPFS.multibase('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal('base32')
+    done()
+  })
+
+  it('isIPFS.multibase should not match an invalid CID (with a typo)', (done) => {
+    const actual = isIPFS.multibase('afybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+})
diff --git a/test/test-subdomain.spec.js b/test/test-subdomain.spec.js
new file mode 100644
index 0000000..aebacc6
--- /dev/null
+++ b/test/test-subdomain.spec.js
@@ -0,0 +1,146 @@
+/* eslint-env mocha */
+'use strict'
+
+const base58 = require('bs58')
+const isIPFS = require('../src/index')
+const expect = require('chai').expect
+
+describe('ipfs subdomain', () => {
+  it('isIPFS.ipfsSubdomain should match a cidv1b32', (done) => {
+    const actual = isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.ipfsSubdomain should match a cidv1b32 with complex ipfs path', (done) => {
+    const actual = isIPFS.ipfsSubdomain('http://bafybeidvtwx54qr44kidymvhfzefzxhgkieigwth6oswk75zhlzjdmunoy.ipfs.dweb.link/linkify-demo.html')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.ipfsSubdomain should not match non-cid subdomains', (done) => {
+    const actual = isIPFS.ipfsSubdomain('http://not-a-cid.ipfs.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipfsSubdomain should not match case-sensitive CID subdomains', (done) => {
+    // Origin forces browsers to lowercase the authority component
+    // so QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR
+    // becomes invalid CID: qmbwqxbekc3p8tqskc98xmwnzrzdtrlmimpl8wbutgsmnr
+    const actual = isIPFS.ipfsSubdomain('http://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR.ipfs.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipfsSubdomain should not match if not under .ipfs. zone', (done) => {
+    // we require explicit convention of putting cidv1b32 under .ipfs. zone
+    // to make it clear content can be safely uplifted and loaded over IPFS
+    const actual = isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipfsSubdomain should not match a buffer data type', (done) => {
+    const actual = isIPFS.ipfsSubdomain(new Buffer(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR')))
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipnsSubdomain should not match .ipfs. zone', (done) => {
+    const actual = isIPFS.ipnsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipnsSubdomain should match a .ipns. zone with cidv1b32', (done) => {
+    const actual = isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.ipnsSubdomain should not match case-sensitive CID subdomains', (done) => {
+    // Origin forces browsers to lowercase the authority component
+    // so QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR
+    // becomes invalid CID: qmbwqxbekc3p8tqskc98xmwnzrzdtrlmimpl8wbutgsmnr
+    const actual = isIPFS.ipnsSubdomain('http://QmcNioXSC1bfJj1dcFErhUfyjFzoX2HodkRccsFFVJJvg8.ipns.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipnsSubdomain should not match .ipns. zone with non-cid subdomain', (done) => {
+    // we do not support opaque strings in subdomains, only  peerids
+    const actual = isIPFS.ipnsSubdomain('http://a-dnslink-website.com.ipns.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipnsSubdomain should not match without .ipns. zone', (done) => {
+    const actual = isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.ipnsSubdomain should not match a buffer data type', (done) => {
+    const actual = isIPFS.ipnsSubdomain(new Buffer(base58.decode('QmNQuBJ8tg4QN6mSLXHekxBbcToPwKxamWNrDdEugxMTDd')))
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.subdomain should match an ipfs subdomain', (done) => {
+    const actual = isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.subdomain should match an ipns subdomain with PeerID as cidv1b32', (done) => {
+    const actual = isIPFS.subdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.subdomain should not match if fqdn does not start with cidv1b32', (done) => {
+    const actual = isIPFS.subdomain('http://www.bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.subdomain should not match if no ipfs/ipns zone', (done) => {
+    const actual = isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.subdomain should not match if ipns peerid is invalid', (done) => {
+    const actual = isIPFS.subdomain('http://not-a-cid.ipns.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.subdomain should not match a buffer data type', (done) => {
+    const actual = isIPFS.subdomain(new Buffer(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR')))
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  /* We keep subdomain logic separate from legacy urlOrPath checks, below is a fail-safe to ensure we keep that behavior */
+
+  it('isIPFS.urlOrPath should not match ipfs url with cidv1b32 subdomain', (done) => {
+    const actual = isIPFS.urlOrPath('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.urlOrPath should not match ipns url', (done) => {
+    const actual = isIPFS.urlOrPath('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.urlOrPath should not match ipns in subdomain', (done) => {
+    const actual = isIPFS.urlOrPath('http://a-dnslink-website.com.ipns.dweb.link')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+})

From db6cc3c6af5b363ba74503d6a0999e1dcd2b280c Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Sun, 22 Jul 2018 23:39:59 +0200
Subject: [PATCH 4/7] refactor: follow new linter rules from latest aegir

---
 src/index.js                | 2 +-
 test/test-cid.spec.js       | 7 ++-----
 test/test-multibase.spec.js | 4 ----
 test/test-multihash.spec.js | 4 ++--
 test/test-path.spec.js      | 8 ++++----
 test/test-subdomain.spec.js | 7 +++----
 test/test-url.spec.js       | 6 +++---
 7 files changed, 15 insertions(+), 23 deletions(-)

diff --git a/src/index.js b/src/index.js
index be394e7..88624d0 100644
--- a/src/index.js
+++ b/src/index.js
@@ -17,7 +17,7 @@ const fqdnProtocolMatch = 2
 function isMultihash (hash) {
   const formatted = convertToString(hash)
   try {
-    const buffer = new Buffer(base58.decode(formatted))
+    const buffer = Buffer.from(base58.decode(formatted))
     multihash.decode(buffer)
     return true
   } catch (e) {
diff --git a/test/test-cid.spec.js b/test/test-cid.spec.js
index 8237d07..f9607f0 100644
--- a/test/test-cid.spec.js
+++ b/test/test-cid.spec.js
@@ -13,13 +13,13 @@ describe('ipfs cid', () => {
   })
 
   it('isIPFS.cid should match a valid CIDv0 (multihash) buffer', (done) => {
-    const actual = isIPFS.cid(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.cid(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(true)
     done()
   })
 
   it('isIPFS.cid should not match a broken CIDv0 buffer', (done) => {
-    const actual = isIPFS.cid(new Buffer('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70'))
+    const actual = isIPFS.cid(Buffer.from('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70'))
     expect(actual).to.equal(false)
     done()
   })
@@ -48,7 +48,6 @@ describe('ipfs cid', () => {
     done()
   })
 
-
   it('isIPFS.cid should not match an invalid CID', (done) => {
     const actual = isIPFS.cid('noop')
     expect(actual).to.equal(false)
@@ -60,6 +59,4 @@ describe('ipfs cid', () => {
     expect(actual).to.equal(false)
     done()
   })
-
-
 })
diff --git a/test/test-multibase.spec.js b/test/test-multibase.spec.js
index bab9b3f..c5c5d7c 100644
--- a/test/test-multibase.spec.js
+++ b/test/test-multibase.spec.js
@@ -1,7 +1,6 @@
 /* eslint-env mocha */
 'use strict'
 
-const base58btc = require('bs58')
 const expect = require('chai').expect
 const isIPFS = require('../src/index')
 
@@ -30,7 +29,6 @@ describe('ipfs base32cid', () => {
     done()
   })
 
-
   it('isIPFS.base32cid should not match an invalid CID', (done) => {
     const actual = isIPFS.base32cid('noop')
     expect(actual).to.equal(false)
@@ -42,7 +40,6 @@ describe('ipfs base32cid', () => {
     expect(actual).to.equal(false)
     done()
   })
-
 })
 
 describe('ipfs multibase', () => {
@@ -69,5 +66,4 @@ describe('ipfs multibase', () => {
     expect(actual).to.equal(false)
     done()
   })
-
 })
diff --git a/test/test-multihash.spec.js b/test/test-multihash.spec.js
index 4c23166..d82a823 100644
--- a/test/test-multihash.spec.js
+++ b/test/test-multihash.spec.js
@@ -13,13 +13,13 @@ describe('ipfs multihash', () => {
   })
 
   it('isIPFS.multihash should match a valid multihash buffer', (done) => {
-    const actual = isIPFS.multihash(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.multihash(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(true)
     done()
   })
 
   it('isIPFS.multihash should not match a buffer', (done) => {
-    const actual = isIPFS.multihash(new Buffer('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70'))
+    const actual = isIPFS.multihash(Buffer.from('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70'))
     expect(actual).to.equal(false)
     done()
   })
diff --git a/test/test-path.spec.js b/test/test-path.spec.js
index 87a2b18..e28057a 100644
--- a/test/test-path.spec.js
+++ b/test/test-path.spec.js
@@ -37,7 +37,7 @@ describe('ipfs path', () => {
   })
 
   it('isIPFS.ipfsPath should not match a buffer data type', (done) => {
-    const actual = isIPFS.ipfsPath(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.ipfsPath(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })
@@ -67,7 +67,7 @@ describe('ipfs path', () => {
   })
 
   it('isIPFS.ipnsPath should not match a buffer data type', (done) => {
-    const actual = isIPFS.ipnsPath(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.ipnsPath(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })
@@ -97,7 +97,7 @@ describe('ipfs path', () => {
   })
 
   it('isIPFS.path should not match a buffer data type', (done) => {
-    const actual = isIPFS.path(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.path(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })
@@ -127,7 +127,7 @@ describe('ipfs path', () => {
   })
 
   it('isIPFS.urlOrPath should not match a buffer data type', (done) => {
-    const actual = isIPFS.ipfsPath(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.ipfsPath(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })
diff --git a/test/test-subdomain.spec.js b/test/test-subdomain.spec.js
index aebacc6..23e0ffe 100644
--- a/test/test-subdomain.spec.js
+++ b/test/test-subdomain.spec.js
@@ -42,7 +42,7 @@ describe('ipfs subdomain', () => {
   })
 
   it('isIPFS.ipfsSubdomain should not match a buffer data type', (done) => {
-    const actual = isIPFS.ipfsSubdomain(new Buffer(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR')))
+    const actual = isIPFS.ipfsSubdomain(Buffer.from(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR')))
     expect(actual).to.equal(false)
     done()
   })
@@ -82,7 +82,7 @@ describe('ipfs subdomain', () => {
   })
 
   it('isIPFS.ipnsSubdomain should not match a buffer data type', (done) => {
-    const actual = isIPFS.ipnsSubdomain(new Buffer(base58.decode('QmNQuBJ8tg4QN6mSLXHekxBbcToPwKxamWNrDdEugxMTDd')))
+    const actual = isIPFS.ipnsSubdomain(Buffer.from(base58.decode('QmNQuBJ8tg4QN6mSLXHekxBbcToPwKxamWNrDdEugxMTDd')))
     expect(actual).to.equal(false)
     done()
   })
@@ -118,7 +118,7 @@ describe('ipfs subdomain', () => {
   })
 
   it('isIPFS.subdomain should not match a buffer data type', (done) => {
-    const actual = isIPFS.subdomain(new Buffer(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR')))
+    const actual = isIPFS.subdomain(Buffer.from(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR')))
     expect(actual).to.equal(false)
     done()
   })
@@ -142,5 +142,4 @@ describe('ipfs subdomain', () => {
     expect(actual).to.equal(false)
     done()
   })
-
 })
diff --git a/test/test-url.spec.js b/test/test-url.spec.js
index a0e61eb..dcfaf1c 100644
--- a/test/test-url.spec.js
+++ b/test/test-url.spec.js
@@ -37,7 +37,7 @@ describe('ipfs url', () => {
   })
 
   it('isIPFS.ipfsUrl should not match a buffer input', (done) => {
-    const actual = isIPFS.ipfsUrl(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.ipfsUrl(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })
@@ -67,7 +67,7 @@ describe('ipfs url', () => {
   })
 
   it('isIPFS.ipnsUrl should not match a buffer input', (done) => {
-    const actual = isIPFS.ipnsUrl(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.ipnsUrl(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })
@@ -97,7 +97,7 @@ describe('ipfs url', () => {
   })
 
   it('isIPFS.url should not match a buffer input', (done) => {
-    const actual = isIPFS.url(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
+    const actual = isIPFS.url(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')))
     expect(actual).to.equal(false)
     done()
   })

From 17f9292112d710509b2a9b3f59d152bd0872ccc5 Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Mon, 23 Jul 2018 00:03:54 +0200
Subject: [PATCH 5/7] fix: remove old node builds from TravisCI

---
 .travis.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 2af65f7..a46ba0e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,6 @@
 
 language: node_js
 node_js:
-  - 4
-  - 5
   - node
 
 # Make sure we have new NPM.

From 8e053264402ddc418cc8ff0d739d66e09c0f9e59 Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Mon, 23 Jul 2018 12:21:45 +0200
Subject: [PATCH 6/7] refactor: remove multibase check

https://github.com/ipfs/is-ipfs/pull/20#discussion_r204333276
---
 README.md                   | 10 +-----
 package.json                |  1 -
 src/index.js                |  1 -
 test/test-cid.spec.js       | 38 ++++++++++++++++++++
 test/test-multibase.spec.js | 69 -------------------------------------
 5 files changed, 39 insertions(+), 80 deletions(-)
 delete mode 100644 test/test-multibase.spec.js

diff --git a/README.md b/README.md
index 372be34..8e42ce9 100644
--- a/README.md
+++ b/README.md
@@ -44,11 +44,6 @@ const isIPFS = require('is-ipfs')
 isIPFS.multihash('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true
 isIPFS.multihash('noop') // false
 
-isIPFS.multibase('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') // 'base32'
-isIPFS.multibase('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7') // 'base58btc'
-isIPFS.multibase('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false (no multibase prefix in CIDv0)
-isIPFS.multibase('noop') // false
-
 isIPFS.cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true (CIDv0)
 isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7') // true (CIDv1)
 isIPFS.cid('noop') // false
@@ -83,6 +78,7 @@ isIPFS.ipfsPath('/ipfs/invalid-hash') // false
 isIPFS.ipnsPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false
 isIPFS.ipnsPath('/ipns/github.com') // true
 
+isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true
 isIPFS.subdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') // true
 isIPFS.subdomain('http://www.bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // false
 isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') // false
@@ -111,10 +107,6 @@ Detection of IPFS Paths and identifiers in URLs is a two-stage process:
 
 Returns `true` if the provided string is a valid `multihash` or `false` otherwise.
 
-### `isIPFS.multibase(cid)`
-
-Returns a string with multibase name if the provided CID has `multibase` prefix or `false` otherwise.
-
 ### `isIPFS.cid(hash)`
 
 Returns `true` if the provided string is a valid `CID` or `false` otherwise.
diff --git a/package.json b/package.json
index 5c3cc26..7e3cf56 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,6 @@
   "dependencies": {
     "bs58": "4.0.1",
     "cids": "0.5.3",
-    "multibase": "0.4.0",
     "multihashes": "0.4.13"
   },
   "devDependencies": {
diff --git a/src/index.js b/src/index.js
index 88624d0..68a5427 100644
--- a/src/index.js
+++ b/src/index.js
@@ -108,7 +108,6 @@ function convertToString (input) {
 
 module.exports = {
   multihash: isMultihash,
-  multibase: isMultibase,
   cid: isCID,
   base32cid: (cid) => (isMultibase(cid) === 'base32' && isCID(cid)),
   ipfsSubdomain: (url) => isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch),
diff --git a/test/test-cid.spec.js b/test/test-cid.spec.js
index f9607f0..8243a48 100644
--- a/test/test-cid.spec.js
+++ b/test/test-cid.spec.js
@@ -60,3 +60,41 @@ describe('ipfs cid', () => {
     done()
   })
 })
+
+describe('ipfs base32cid', () => {
+  it('isIPFS.base32cid should not match a valid CIDv0 (multihash in base58btc)', (done) => {
+    const actual = isIPFS.base32cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match a valid CIDv1 in base58btc', (done) => {
+    const actual = isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.base32cid should match a valid URL-safe CIDv1 in Base32', (done) => {
+    const actual = isIPFS.base32cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal(true)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match an invalid CID (with a typo)', (done) => {
+    const actual = isIPFS.base32cid('afybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match an invalid CID', (done) => {
+    const actual = isIPFS.base32cid('noop')
+    expect(actual).to.equal(false)
+    done()
+  })
+
+  it('isIPFS.base32cid should not match an invalid CID data type', (done) => {
+    const actual = isIPFS.base32cid(4)
+    expect(actual).to.equal(false)
+    done()
+  })
+})
diff --git a/test/test-multibase.spec.js b/test/test-multibase.spec.js
deleted file mode 100644
index c5c5d7c..0000000
--- a/test/test-multibase.spec.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* eslint-env mocha */
-'use strict'
-
-const expect = require('chai').expect
-const isIPFS = require('../src/index')
-
-describe('ipfs base32cid', () => {
-  it('isIPFS.base32cid should not match a valid CIDv0 (multihash in base58btc)', (done) => {
-    const actual = isIPFS.base32cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
-    expect(actual).to.equal(false)
-    done()
-  })
-
-  it('isIPFS.base32cid should not match a valid CIDv1 in base58btc', (done) => {
-    const actual = isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7')
-    expect(actual).to.equal(true)
-    done()
-  })
-
-  it('isIPFS.base32cid should match a valid URL-safe CIDv1 in Base32', (done) => {
-    const actual = isIPFS.base32cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
-    expect(actual).to.equal(true)
-    done()
-  })
-
-  it('isIPFS.base32cid should not match an invalid CID (with a typo)', (done) => {
-    const actual = isIPFS.base32cid('afybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
-    expect(actual).to.equal(false)
-    done()
-  })
-
-  it('isIPFS.base32cid should not match an invalid CID', (done) => {
-    const actual = isIPFS.base32cid('noop')
-    expect(actual).to.equal(false)
-    done()
-  })
-
-  it('isIPFS.base32cid should not match an invalid CID data type', (done) => {
-    const actual = isIPFS.base32cid(4)
-    expect(actual).to.equal(false)
-    done()
-  })
-})
-
-describe('ipfs multibase', () => {
-  it('isIPFS.multibase should return false for a valid CIDv0 (raw multihash in base58btc, nor multibase prefix)', (done) => {
-    const actual = isIPFS.multibase('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
-    expect(actual).to.equal(false)
-    done()
-  })
-
-  it('isIPFS.multibase should match a valid CIDv1 in base58btc', (done) => {
-    const actual = isIPFS.multibase('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7')
-    expect(actual).to.equal('base58btc')
-    done()
-  })
-
-  it('isIPFS.multibase should match CIDv1 in Base32', (done) => {
-    const actual = isIPFS.multibase('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
-    expect(actual).to.equal('base32')
-    done()
-  })
-
-  it('isIPFS.multibase should not match an invalid CID (with a typo)', (done) => {
-    const actual = isIPFS.multibase('afybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va')
-    expect(actual).to.equal(false)
-    done()
-  })
-})

From dcef7348af711880bfd3306f5b6775926957404b Mon Sep 17 00:00:00 2001
From: Marcin Rataj <lidel@lidel.org>
Date: Mon, 23 Jul 2018 13:22:25 +0200
Subject: [PATCH 7/7] refactor: addressing PR review

---
 README.md    | 2 +-
 package.json | 1 +
 src/index.js | 9 ++++++---
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 8e42ce9..379254c 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,7 @@ isIPFS.ipnsSubdomain('http://foo-bar.ipns.dweb.link') // false (not a PeerID)
 
 # API
 
-A suite of util methods provides efficient validation.
+A suite of util methods that provides efficient validation.
 
 Detection of IPFS Paths and identifiers in URLs is a two-stage process:
 1.  `urlPattern`/`pathPattern`/`subdomainPattern` regex is applied to quickly identify potential candidates
diff --git a/package.json b/package.json
index 7e3cf56..5c3cc26 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
   "dependencies": {
     "bs58": "4.0.1",
     "cids": "0.5.3",
+    "multibase": "0.4.0",
     "multihashes": "0.4.13"
   },
   "devDependencies": {
diff --git a/src/index.js b/src/index.js
index 68a5427..acfef52 100644
--- a/src/index.js
+++ b/src/index.js
@@ -106,13 +106,16 @@ function convertToString (input) {
   return false
 }
 
+const ipfsSubdomain = (url) => isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch)
+const ipnsSubdomain = (url) => isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch)
+
 module.exports = {
   multihash: isMultihash,
   cid: isCID,
   base32cid: (cid) => (isMultibase(cid) === 'base32' && isCID(cid)),
-  ipfsSubdomain: (url) => isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch),
-  ipnsSubdomain: (url) => isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch),
-  subdomain: (url) => (isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch) || isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch)),
+  ipfsSubdomain: ipfsSubdomain,
+  ipnsSubdomain: ipnsSubdomain,
+  subdomain: (url) => (ipfsSubdomain(url) || ipnsSubdomain(url)),
   subdomainPattern: fqdnPattern,
   ipfsUrl: (url) => isIpfs(url, urlPattern),
   ipnsUrl: (url) => isIpns(url, urlPattern),