Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add types #39

Merged
merged 11 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
language: node_js
cache: npm
dist: bionic
stages:
- check
- test
- cov

branches:
only:
- master
- /^release\/.*$/

lidel marked this conversation as resolved.
Show resolved Hide resolved
node_js:
- 'lts/*'
- 'node'
Expand All @@ -21,7 +27,6 @@ jobs:
include:
- stage: check
script:
- npx aegir commitlint --travis
- npx aegir dep-check
- npm run lint

Expand All @@ -35,7 +40,7 @@ jobs:
name: firefox
addons:
firefox: latest
script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
script: npx aegir test -t browser -t webworker -- --browser firefox

- stage: test
name: electron-main
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"src",
"dist"
],
"types": "./dist/src/index.d.ts",
"main": "src/index.js",
"browser": {
"fs": false
Expand All @@ -29,28 +30,27 @@
"url": "https://github.com/ipfs/is-ipfs.git"
},
"scripts": {
"prepare": "aegir build --no-bundle",
Copy link
Member

@lidel lidel Mar 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hugomrdias What does --no-bundle do?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It skips the browser bundle step as you don't need it during development, i.e. it only generates types from the jsdoc comments.

"test:node": "aegir test --target node",
"test:browser": "aegir test --target browser",
"test": "aegir test",
"prepublishOnly": "aegir build",
"lint": "aegir lint && aegir lint-package-json",
"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"
"release-major": "aegir release --type major"
},
"dependencies": {
"cids": "^1.1.5",
"iso-url": "^1.0.0",
"iso-url": "^1.1.0",
"mafmt": "^8.0.4",
"multiaddr": "^8.1.2",
"multibase": "^3.1.1",
"multihashes": "^3.1.2",
"multibase": "^4.0.1",
"multihashes": "^4.0.0",
"uint8arrays": "^2.0.5"
},
"devDependencies": {
"aegir": "^30.3.0",
"aegir": "^31.0.0",
"pre-commit": "^1.2.2"
},
"engines": {
Expand Down
74 changes: 72 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,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])$/

/**
* @param {*} hash
*/
function isMultihash (hash) {
const formatted = convertToString(hash)
try {
Expand All @@ -31,6 +34,9 @@ function isMultihash (hash) {
}
}

/**
* @param {*} hash
*/
function isMultibase (hash) {
try {
return multibase.isEncoded(hash)
Expand All @@ -39,6 +45,9 @@ function isMultibase (hash) {
}
}

/**
* @param {*} hash
*/
function isCID (hash) {
try {
new CID(hash) // eslint-disable-line no-new
Expand All @@ -48,6 +57,9 @@ function isCID (hash) {
}
}

/**
* @param {*} input
*/
function isMultiaddr (input) {
if (!input) return false
if (Multiaddr.isMultiaddr(input)) return true
Expand All @@ -59,10 +71,19 @@ function isMultiaddr (input) {
}
}

/**
* @param {string | Uint8Array | Multiaddr} input
*/
function isPeerMultiaddr (input) {
return isMultiaddr(input) && (mafmt.P2P.matches(input) || mafmt.DNS.matches(input))
}

/**
* @param {string | Uint8Array} input
* @param {RegExp | string} pattern
* @param {number} [protocolMatch=1]
* @param {number} [hashMatch=2]
*/
function isIpfs (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch = defaultHashMath) {
const formatted = convertToString(input)
if (!formatted) {
Expand All @@ -83,14 +104,21 @@ function isIpfs (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch
if (hash && pattern === subdomainGatewayPattern) {
// when doing checks for subdomain context
// ensure hash is case-insensitive
// (browsers force-lowercase authority compotent anyway)
// (browsers force-lowercase authority component anyway)
hash = hash.toLowerCase()
}

return isCID(hash)
}

function isIpns (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch) {
/**
*
* @param {string | Uint8Array} input
* @param {string | RegExp} pattern
* @param {number} [protocolMatch=1]
* @param {number} [hashMatch=1]
*/
function isIpns (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch = defaultHashMath) {
const formatted = convertToString(input)
if (!formatted) {
return false
Expand Down Expand Up @@ -133,10 +161,16 @@ function isIpns (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch
return true
}

/**
* @param {any} input
*/
function isString (input) {
return typeof input === 'string'
}

/**
* @param {Uint8Array | string} input
*/
function convertToString (input) {
if (input instanceof Uint8Array) {
return uint8ArrayToString(input, 'base58btc')
Expand All @@ -149,21 +183,45 @@ function convertToString (input) {
return false
}

/**
* @param {string | Uint8Array} url
*/
const ipfsSubdomain = (url) => isIpfs(url, subdomainGatewayPattern, subdomainProtocolMatch, subdomainIdMatch)
/**
* @param {string | Uint8Array} url
*/
const ipnsSubdomain = (url) => isIpns(url, subdomainGatewayPattern, subdomainProtocolMatch, subdomainIdMatch)
/**
* @param {string | Uint8Array} url
*/
const subdomain = (url) => ipfsSubdomain(url) || ipnsSubdomain(url)

/**
* @param {string | Uint8Array} url
*/
const ipfsUrl = (url) => isIpfs(url, pathGatewayPattern) || ipfsSubdomain(url)
/**
* @param {string | Uint8Array} url
*/
const ipnsUrl = (url) => isIpns(url, pathGatewayPattern) || ipnsSubdomain(url)
/**
* @param {string | Uint8Array} url
*/
const url = (url) => ipfsUrl(url) || ipnsUrl(url) || subdomain(url)

/**
* @param {string | Uint8Array} path
*/
const path = (path) => isIpfs(path, pathPattern) || isIpns(path, pathPattern)

module.exports = {
multihash: isMultihash,
multiaddr: isMultiaddr,
peerMultiaddr: isPeerMultiaddr,
cid: isCID,
/**
* @param {CID | string | Uint8Array} cid
*/
base32cid: (cid) => (isMultibase(cid) === 'base32' && isCID(cid)),
ipfsSubdomain,
ipnsSubdomain,
Expand All @@ -173,10 +231,22 @@ module.exports = {
ipnsUrl,
url,
pathGatewayPattern: pathGatewayPattern,
/**
* @param {string | Uint8Array} path
*/
ipfsPath: (path) => isIpfs(path, pathPattern),
/**
* @param {string | Uint8Array} path
*/
ipnsPath: (path) => isIpns(path, pathPattern),
path,
pathPattern,
/**
* @param {string | Uint8Array} x
*/
urlOrPath: (x) => url(x) || path(x),
/**
* @param {string | Uint8Array | CID} path
*/
cidPath: path => isString(path) && !isCID(path) && isIpfs(`/ipfs/${path}`, pathPattern)
}
1 change: 1 addition & 0 deletions test/test-cid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ describe('ipfs base32cid', () => {
})

it('isIPFS.base32cid should not match an invalid CID data type', (done) => {
// @ts-ignore data type is invalid
const actual = isIPFS.base32cid(4)
expect(actual).to.equal(false)
done()
Expand Down
1 change: 1 addition & 0 deletions test/test-multiaddr.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ describe('ipfs peerMultiaddr', () => {
})

it('isIPFS.peerMultiaddr should not match an invalid multiaddr data type', (done) => {
// @ts-ignore data type is invalid
const actual = isIPFS.peerMultiaddr(4)
expect(actual).to.equal(false)
done()
Expand Down
1 change: 1 addition & 0 deletions test/test-path.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ describe('ipfs path', () => {
})

it('isIPFS.cidPath should not match a non string', () => {
// @ts-ignore data type is invalid
const actual = isIPFS.cidPath({ toString: () => 'QmYHNYAaYK5hm3ZhZFx5W9H6xydKDGimjdgJMrMSdnctEm/path/to/file' })
expect(actual).to.equal(false)
})
Expand Down
10 changes: 10 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./node_modules/aegir/src/config/tsconfig.aegir.json",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not assume where exactly aegir is.

Suggested change
"extends": "./node_modules/aegir/src/config/tsconfig.aegir.json",
"extends": "aegir/src/config/tsconfig.aegir.json",

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That started failing recently and we don't know why yet.

Copy link
Member Author

@achingbrain achingbrain Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typescript team broke this a minor release last week:

> [email protected] prepare
> aegir build --no-bundle

[11:11:07] Clean ./dist [started]
[11:11:07] Clean ./dist [completed]
[11:11:07] Generate types [started]
tsconfig-types.aegir.json:1:12 - error TS6053: File 'aegir/src/config/tsconfig.aegir.json' not found.

😭

"compilerOptions": {
"outDir": "dist"
},
"include": [
"src",
"test"
]
}