From a5d55d948ed521b126a8ed757e864fb0325dba09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 6 Sep 2022 10:45:21 +0200 Subject: [PATCH 1/4] Add failing tests for recongnizing `ipfs://` URIs - Fixes #48 --- test/test-path.spec.js | 6 ++++++ test/test-url.spec.js | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test/test-path.spec.js b/test/test-path.spec.js index 2aa74d8..8858330 100644 --- a/test/test-path.spec.js +++ b/test/test-path.spec.js @@ -108,6 +108,12 @@ describe('ipfs path', () => { done() }) + it('isIPFS.urlOrPath should match an IANA-schema compliant ipfs url', (done) => { + const actual = isIPFS.urlOrPath('ipfs://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm') + expect(actual).to.equal(true) + done() + }) + it('isIPFS.urlOrPath should match ipns url', (done) => { const actual = isIPFS.urlOrPath('http://ipfs.io/ipns/foo.bar.com') expect(actual).to.equal(true) diff --git a/test/test-url.spec.js b/test/test-url.spec.js index d45acff..dfad1b4 100644 --- a/test/test-url.spec.js +++ b/test/test-url.spec.js @@ -12,6 +12,12 @@ describe('ipfs url', () => { done() }) + it('isIPFS.ipfsUrl should match an IANA-schema compliant ipfs uri', (done) => { + const actual = isIPFS.ipfsUrl('ipfs://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm') + expect(actual).to.equal(true) + done() + }) + it('isIPFS.ipfsUrl should match a complex ipfs url', (done) => { const actual = isIPFS.ipfsUrl('http://ipfs.alexandria.media/ipfs/QmeWz9YZEeNFXQhHg4PnR5ZiNr5isttgi5n1tc1eD5EfGU/content/index.html?arg=val#hash') expect(actual).to.equal(true) @@ -72,12 +78,24 @@ describe('ipfs url', () => { done() }) + it('isIPFS.ipnsUrl should not match an IANA-schema compliant ipfs uri', (done) => { + const actual = isIPFS.ipnsUrl('ipfs://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm') + expect(actual).to.equal(false) + done() + }) + it('isIPFS.url should match an ipfs url', (done) => { const actual = isIPFS.url('http://ipfs.io/ipfs/QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm') expect(actual).to.equal(true) done() }) + it('isIPFS.url should match an IANA-schema compliant ipfs uri', (done) => { + const actual = isIPFS.url('ipfs://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm') + expect(actual).to.equal(true) + done() + }) + it('isIPFS.url should match an ipns url', (done) => { const actual = isIPFS.url('http://ipfs.io/ipns/github.com/') expect(actual).to.equal(true) From 64332de30a867172d55195c994ae78992a66bda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 6 Sep 2022 11:17:14 +0200 Subject: [PATCH 2/4] Match `ipfs://...` - Fixes #48 --- README.md | 3 +++ src/index.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 61bd232..cf84bc2 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ isIPFS.url('https://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va. isIPFS.url('http://en.wikipedia-on-ipfs.org.ipfs.localhost:8080') // true isIPFS.url('https://github.com/ipfs/js-ipfs/blob/master/README.md') // false isIPFS.url('https://google.com') // false +isIPFS.url('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.path('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.path('/ipfs/QmbcBPAwCDxRMB1Qe7CRQmxdrTSkxKwM9y6rZw2FjGtbsb/?weird-filename=test.jpg') // true @@ -71,9 +72,11 @@ isIPFS.urlOrPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.urlOrPath('/ipns/github.com') // true isIPFS.urlOrPath('https://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true isIPFS.urlOrPath('https://google.com') // false +isIPFS.urlOrPath('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipfsUrl('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipfsUrl('https://ipfs.io/ipfs/invalid-hash') // false +isIPFS.ipfsUrl('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipnsUrl('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false isIPFS.ipnsUrl('https://ipfs.io/ipns/github.com') // true diff --git a/src/index.js b/src/index.js index 64d1e3f..646eced 100644 --- a/src/index.js +++ b/src/index.js @@ -22,6 +22,9 @@ const subdomainProtocolMatch = 2 // Fully qualified domain name (FQDN) that has an explicit .tld suffix const fqdnWithTld = /^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])\.)+([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$/ +// URI IANA-scheme +const uriSchemePattern = /^(ipfs):\/\/([^/?#]+)/ + /** * @param {*} hash */ @@ -210,7 +213,7 @@ const subdomain = (url) => ipfsSubdomain(url) || ipnsSubdomain(url) /** * @param {string | Uint8Array} url */ -const ipfsUrl = (url) => isIpfs(url, pathGatewayPattern) || ipfsSubdomain(url) +const ipfsUrl = (url) => isIpfs(url, pathGatewayPattern) || ipfsSubdomain(url) || isIpfs(url, uriSchemePattern) /** * @param {string | Uint8Array} url */ From e3c7f1fd547725cc858a70be024d117053eddfd8 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Tue, 6 Feb 2024 18:10:13 +0100 Subject: [PATCH 3/4] fix: support ipns:// urls --- src/index.ts | 4 ++-- test/test-path.spec.ts | 9 +++++++-- test/test-url.spec.ts | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 7908632..d3c6bdd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -117,7 +117,7 @@ const subdomainProtocolMatch = 2 const fqdnWithTld = /^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])\.)+([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$/ // URI IANA-scheme -const uriSchemePattern = /^(ipfs):\/\/([^/?#]+)/ +const uriSchemePattern = /^(ip[fn]s):\/\/([^/?#]+)/ function isMultihash (hash: Uint8Array | string): boolean { const formatted = convertToString(hash) @@ -329,7 +329,7 @@ export const ipfsUrl = (url: string | Uint8Array): boolean => isIpfs(url, pathGa * Returns `true` if the provided string is a valid IPNS url or `false` * otherwise. */ -export const ipnsUrl = (url: string | Uint8Array): boolean => isIpns(url, pathGatewayPattern) || ipnsSubdomain(url) +export const ipnsUrl = (url: string | Uint8Array): boolean => isIpns(url, pathGatewayPattern) || ipnsSubdomain(url) || isIpns(url, uriSchemePattern) /** * Returns `true` if the provided string is a valid IPFS or IPNS url or `false` diff --git a/test/test-path.spec.ts b/test/test-path.spec.ts index 344b9ac..e4c0c8e 100644 --- a/test/test-path.spec.ts +++ b/test/test-path.spec.ts @@ -159,7 +159,12 @@ describe('ipfs path', () => { }) it('isIPFS.cidPath should not match an IPFS path', () => { - const actual = isIPFS.cidPath('/ipfs/QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm') - expect(actual).to.equal(false) + expect(isIPFS.cidPath('/ipfs/QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm')).to.be.false() + }) +}) + +describe('ipns path', () => { + it('isIPFS.urlOrPath should match an IANA-schema compliant ipns url', () => { + expect(isIPFS.urlOrPath('ipns://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm')).to.be.true() }) }) diff --git a/test/test-url.spec.ts b/test/test-url.spec.ts index 7a72ecc..3523e3c 100644 --- a/test/test-url.spec.ts +++ b/test/test-url.spec.ts @@ -119,3 +119,17 @@ describe('ipfs url', () => { done() }) }) + +describe('ipns url', () => { + it('isIPFS.ipnsUrl should match an IANA-schema compliant ipns uri', () => { + expect(isIPFS.ipnsUrl('ipns://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm')).to.be.true() + }) + + it('isIPFS.ipnsUrl should not match an IANA-schema compliant ipfs uri', () => { + expect(isIPFS.ipnsUrl('ipfs://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm')).to.be.false() + }) + + it('isIPFS.url should match an IANA-schema compliant ipns uri', () => { + expect(isIPFS.url('ipns://QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm')).to.be.true() + }) +}) From a3168eb0ebf6c66a6ea5884115e9bd0810af9136 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Tue, 6 Feb 2024 18:16:37 +0100 Subject: [PATCH 4/4] chore: update examples --- README.md | 7 ++++--- src/index.ts | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9dd4d5f..fae9366 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ isIPFS.url('https://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va. isIPFS.url('http://en.wikipedia-on-ipfs.org.ipfs.localhost:8080') // true isIPFS.url('https://github.com/ipfs/js-ipfs/blob/master/README.md') // false isIPFS.url('https://google.com') // false -isIPFS.url('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.path('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.path('/ipfs/QmbcBPAwCDxRMB1Qe7CRQmxdrTSkxKwM9y6rZw2FjGtbsb/?weird-filename=test.jpg') // true @@ -47,17 +46,19 @@ isIPFS.path('/ipfs/js-ipfs/blob/master/README.md') // false isIPFS.urlOrPath('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.urlOrPath('https://ipfs.io/ipns/github.com') // true isIPFS.urlOrPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true +isIPFS.urlOrPath('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.urlOrPath('/ipns/github.com') // true +isIPFS.urlOrPath('ipns://github.com') // true isIPFS.urlOrPath('https://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true isIPFS.urlOrPath('https://google.com') // false -isIPFS.urlOrPath('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipfsUrl('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipfsUrl('https://ipfs.io/ipfs/invalid-hash') // false -isIPFS.ipfsUrl('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true +isIPFS.ipfsUrl('ipfs://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipnsUrl('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false isIPFS.ipnsUrl('https://ipfs.io/ipns/github.com') // true +isIPFS.ipnsUrl('ipns://ipfs.io/ipns/github.com') // true isIPFS.ipfsPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.ipfsPath('/ipfs/invalid-hash') // false diff --git a/src/index.ts b/src/index.ts index d3c6bdd..2ba6120 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,15 +40,19 @@ * isIPFS.urlOrPath('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true * isIPFS.urlOrPath('https://ipfs.io/ipns/github.com') // true * isIPFS.urlOrPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true + * isIPFS.urlOrPath('ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true * isIPFS.urlOrPath('/ipns/github.com') // true + * isIPFS.urlOrPath('ipns://github.com') // true * isIPFS.urlOrPath('https://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true * isIPFS.urlOrPath('https://google.com') // false * * isIPFS.ipfsUrl('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true * isIPFS.ipfsUrl('https://ipfs.io/ipfs/invalid-hash') // false + * isIPFS.ipfsUrl('ipfs://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true * * isIPFS.ipnsUrl('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false * isIPFS.ipnsUrl('https://ipfs.io/ipns/github.com') // true + * isIPFS.ipnsUrl('ipns://ipfs.io/ipns/github.com') // true * * isIPFS.ipfsPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true * isIPFS.ipfsPath('/ipfs/invalid-hash') // false