diff --git a/.aegir.js b/.aegir.cjs similarity index 100% rename from .aegir.js rename to .aegir.cjs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c9772b6 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,125 @@ +name: test & maybe release +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present lint + - run: npm run --if-present dep-check + + test-node: + needs: check + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + node: [16] + fail-fast: true + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node }} + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:node + - uses: codecov/codecov-action@v1 + + test-chrome: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:chrome + + test-chrome-webworker: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:chrome-webworker + + test-firefox: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:firefox + + test-firefox-webworker: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:firefox-webworker + + test-electron-main: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npx xvfb-maybe npm run --if-present test:electron-main + + test-electron-renderer: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npx xvfb-maybe npm run --if-present test:electron-renderer + + release: + needs: [test-node, test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-electron-main, test-electron-renderer] + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v2.4.0 + with: + fetch-depth: 0 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - uses: ipfs/aegir/actions/docker-login@master + with: + docker-token: ${{ secrets.DOCKER_USERNAME }} + docker-username: ${{ secrets.DOCKER_USERNAME }} + - run: npm run --if-present release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0e3ac6b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ -language: node_js -cache: npm -dist: bionic - -branches: - only: - - master - - /^release\/.*$/ - -stages: - - check - - test - - cov - -node_js: - - 'lts/*' - - 'node' - -os: - - linux - - osx - - windows - -script: npm run test:node -- --cov -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 - - - 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 -- --browser firefox - -notifications: - email: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b0b237f --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..14478a3 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..749aa1e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 95b7527..a3a0dce 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -js-mafmt -======== +# js-mafmt [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) @@ -10,20 +9,16 @@ js-mafmt > Javascript implementation of multiaddr validation -## Lead Maintainer - -[Vasco Santos](https://github.com/vasco-santos). - ## Install ```sh -npm install mafmt +npm install @multiformats/mafmt ``` ## Usage ```js -const mafmt = require('mafmt') +import * as mafmt from 'multiformats/mafmt' mafmt.DNS.matches('/dns4/ipfs.io') // true ``` diff --git a/package.json b/package.json index e3ae6e7..94586d8 100644 --- a/package.json +++ b/package.json @@ -1,62 +1,53 @@ { - "name": "mafmt", + "name": "@multiformats/mafmt", "version": "10.0.0", "description": "A multiaddr validator", - "leadMaintainer": "Vasco Santos ", - "main": "src/index.js", - "types": "dist/src/index.d.ts", - "scripts": { - "lint": "aegir ts -p check && aegir lint", - "build": "aegir build", - "test": "aegir test", - "test:node": "aegir test --target node", - "test:browser": "aegir test --target browser", - "test:types": "npx tsc", - "release": "aegir release", - "release-minor": "aegir release --type minor", - "release-major": "aegir release --type major", - "coverage": "aegir coverage", - "coverage-publish": "aegir coverage publish" + "type": "module", + "types": "./dist/src/index.d.ts", + "exports": { + ".": { + "import": "./dist/src/index.js" + } }, - "pre-push": [ - "lint" + "files": [ + "src", + "dist/src", + "!dist/test", + "!**/*.tsbuildinfo" ], + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "sourceType": "module" + } + }, + "scripts": { + "lint": "aegir lint", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", + "build": "tsc", + "pretest": "npm run build", + "test": "aegir test -f ./dist/test/**/*.js", + "test:chrome": "npm run test -- -t browser", + "test:chrome-webworker": "npm run test -- -t webworker", + "test:firefox": "npm run test -- -t browser -- --browser firefox", + "test:firefox-webworker": "npm run test -- -t webworker -- --browser firefox", + "test:node": "npm run test -- -t node --cov", + "test:electron-main": "npm run test -- -t electron-main", + "release": "semantic-release" + }, "repository": "github:multiformats/js-mafmt", "keywords": [ "multiaddr" ], - "license": "MIT", + "license": "(Apache-2.0 OR MIT)", "bugs": "https://github.com/multiformats/js-mafmt/issues", "homepage": "https://github.com/multiformats/js-mafmt#readme", "devDependencies": { - "aegir": "^35.0.1", + "aegir": "^36.1.3", "uint8arrays": "^3.0.0", "util": "^0.12.3" }, "dependencies": { - "multiaddr": "^10.0.0" - }, - "files": [ - "src", - "dist", - "!dist/*.tsbuildinfo" - ], - "contributors": [ - "David Dias ", - "Vasco Santos ", - "Jacob Heun ", - "dmitriy ryajov ", - "Jeromy Johnson ", - "Maciej Krüger ", - "Alan Shaw ", - "Alex Potsides ", - "Jeromy ", - "dignifiedquire ", - "victorbjelkholm ", - "Hugo Dias ", - "Cayman ", - "wqsz7xn <67990210+wqsz7xn@users.noreply.github.com>", - "Marcin Rataj ", - "João Antunes " - ] + "@multiformats/multiaddr": "^10.1.1" + } } diff --git a/src/index.js b/src/index.ts similarity index 59% rename from src/index.js rename to src/index.ts index ad368c9..7edb0c2 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,80 +1,84 @@ -'use strict' - -const { Multiaddr } = require('multiaddr') - -/** - * @typedef {import('./types').MatchesFunction} MatchesFunction - * @typedef {import('./types').PartialMatchesFunction} PartialMatchesFunction - * @typedef {import('./types').Mafmt} Mafmt - */ +import { Multiaddr } from '@multiformats/multiaddr' + +// eslint-disable-next-line etc/prefer-interface +export type MatchesFunction = (a: string | Uint8Array | Multiaddr) => boolean +// eslint-disable-next-line etc/prefer-interface +export type PartialMatchesFunction = (protos: string[]) => boolean | string[] | null + +export interface Mafmt { + toString: () => string + input?: Array<(Mafmt | (() => Mafmt))> + matches: MatchesFunction + partialMatch: PartialMatchesFunction +} /* * Valid combinations */ -const DNS4 = base('dns4') -const DNS6 = base('dns6') -const DNSADDR = base('dnsaddr') -const DNS = or( +export const DNS4 = base('dns4') +export const DNS6 = base('dns6') +export const DNSADDR = base('dnsaddr') +export const DNS = or( base('dns'), DNSADDR, DNS4, DNS6 ) -const IP = or(base('ip4'), base('ip6')) -const TCP = or( +export const IP = or(base('ip4'), base('ip6')) +export const TCP = or( and(IP, base('tcp')), and(DNS, base('tcp')) ) -const UDP = and(IP, base('udp')) -const UTP = and(UDP, base('utp')) +export const UDP = and(IP, base('udp')) +export const UTP = and(UDP, base('utp')) -const QUIC = and(UDP, base('quic')) +export const QUIC = and(UDP, base('quic')) -const WebSockets = or( +export const WebSockets = or( and(TCP, base('ws')), and(DNS, base('ws')) ) -const WebSocketsSecure = or( +export const WebSocketsSecure = or( and(TCP, base('wss')), and(DNS, base('wss')) ) -const HTTP = or( +export const HTTP = or( and(TCP, base('http')), and(IP, base('http')), and(DNS, base('http')) ) -const HTTPS = or( +export const HTTPS = or( and(TCP, base('https')), and(IP, base('https')), and(DNS, base('https')) ) -const WebRTCStar = or( +export const WebRTCStar = or( and(WebSockets, base('p2p-webrtc-star'), base('p2p')), and(WebSocketsSecure, base('p2p-webrtc-star'), base('p2p')), and(WebSockets, base('p2p-webrtc-star')), and(WebSocketsSecure, base('p2p-webrtc-star')) ) -const WebSocketStar = or( +export const WebSocketStar = or( and(WebSockets, base('p2p-websocket-star'), base('p2p')), and(WebSocketsSecure, base('p2p-websocket-star'), base('p2p')), and(WebSockets, base('p2p-websocket-star')), and(WebSocketsSecure, base('p2p-websocket-star')) ) -const WebRTCDirect = or( +export const WebRTCDirect = or( and(HTTP, base('p2p-webrtc-direct'), base('p2p')), and(HTTPS, base('p2p-webrtc-direct'), base('p2p')), and(HTTP, base('p2p-webrtc-direct')), and(HTTPS, base('p2p-webrtc-direct')) ) -const Reliable = or( +export const Reliable = or( WebSockets, WebSocketsSecure, HTTP, @@ -88,7 +92,7 @@ const Reliable = or( ) // Unlike ws-star, stardust can run over any transport thus removing the requirement for websockets (but don't even think about running a stardust server over webrtc-star ;) ) -const Stardust = or( +export const Stardust = or( and(Reliable, base('p2p-stardust'), base('p2p')), and(Reliable, base('p2p-stardust')) ) @@ -114,9 +118,9 @@ const CircuitRecursive = () => or( _Circuit ) -const Circuit = CircuitRecursive() +export const Circuit = CircuitRecursive() -const P2P = or( +export const P2P = or( and(Circuit, _P2P, Circuit), and(_P2P, Circuit), and(Circuit, _P2P), @@ -124,50 +128,23 @@ const P2P = or( _P2P ) -module.exports = { - DNS, - DNS4, - DNS6, - DNSADDR, - IP, - TCP, - UDP, - QUIC, - UTP, - HTTP, - HTTPS, - WebSockets, - WebSocketsSecure, - WebSocketStar, - WebRTCStar, - WebRTCDirect, - Reliable, - Stardust, - Circuit, - P2P, - IPFS: P2P -} +export const IPFS = P2P /* * Validation funcs */ -/** - * @param {PartialMatchesFunction} partialMatch - */ -function makeMatchesFunction (partialMatch) { - /** - * @type {MatchesFunction} - */ - function matches (a) { - if (!Multiaddr.isMultiaddr(a)) { - try { - a = new Multiaddr(a) - } catch (err) { // catch error - return false // also if it's invalid it's propably not matching as well so return false - } +function makeMatchesFunction (partialMatch: PartialMatchesFunction) { + function matches (a: string | Uint8Array | Multiaddr): boolean { + let ma + + try { + ma = new Multiaddr(a) + } catch (err: any) { // catch error + return false // also if it's invalid it's propably not matching as well so return false } - const out = partialMatch(a.protoNames()) + + const out = partialMatch(ma.protoNames()) if (out === null) { return false } @@ -182,21 +159,13 @@ function makeMatchesFunction (partialMatch) { return matches } -/** - * @param {Array Mafmt)>} args - * @returns {Mafmt} - */ -function and (...args) { - /** - * @type {PartialMatchesFunction} - */ - function partialMatch (a) { +function and (...args: Array Mafmt)>): Mafmt { + function partialMatch (a: string[]): boolean | string[] | null { if (a.length < args.length) { return null } - /** @type {boolean | string[] | null} */ - let out = a + let out: boolean | string[] | null = a args.some((arg) => { out = typeof arg === 'function' @@ -225,21 +194,14 @@ function and (...args) { } } -/** - * @param {Array Mafmt)>} args - * @returns {Mafmt} - */ -function or (...args) { - /** - * @type {PartialMatchesFunction} - */ - function partialMatch (a) { +function or (...args: Array Mafmt)>): Mafmt { + function partialMatch (a: string[]): boolean | string[] | null { let out = null args.some((arg) => { const res = typeof arg === 'function' ? arg().partialMatch(a) : arg.partialMatch(a) - if (res) { + if (res != null) { out = res return true } @@ -259,27 +221,16 @@ function or (...args) { return result } -/** - * @param {string} n - * @returns {Mafmt} - */ -function base (n) { +function base (n: string): Mafmt { const name = n - /** - * @type {MatchesFunction} - */ - function matches (a) { - let ma + function matches (a: string | Uint8Array | Multiaddr) { + let ma: Multiaddr - if (typeof a === 'string' || a instanceof Uint8Array) { - try { - ma = new Multiaddr(a) - } catch (err) { // catch error - return false // also if it's invalid it's probably not matching as well so return false - } - } else { - ma = a + try { + ma = new Multiaddr(a) + } catch (err: any) { // catch error + return false // also if it's invalid it's probably not matching as well so return false } const pnames = ma.protoNames() @@ -289,10 +240,7 @@ function base (n) { return false } - /** - * @type {PartialMatchesFunction} - */ - function partialMatch (protos) { + function partialMatch (protos: string[]): boolean | string[] | null { if (protos.length === 0) { return null } diff --git a/src/types.d.ts b/src/types.d.ts deleted file mode 100644 index 7cf3803..0000000 --- a/src/types.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Multiaddr } from 'multiaddr' - -// eslint-disable-next-line etc/prefer-interface -export type MatchesFunction = (a: string | Uint8Array | Multiaddr) => boolean -// eslint-disable-next-line etc/prefer-interface -export type PartialMatchesFunction = (protos: string[]) => boolean | string[] | null - -export interface Mafmt { - toString: () => string - input?: Array<(Mafmt | (() => Mafmt))> - matches: MatchesFunction - partialMatch: PartialMatchesFunction -} diff --git a/test/index.spec.js b/test/index.spec.ts similarity index 92% rename from test/index.spec.js rename to test/index.spec.ts index dcdfcea..c170f10 100644 --- a/test/index.spec.js +++ b/test/index.spec.ts @@ -1,12 +1,9 @@ /* eslint-env mocha */ - -'use strict' - -const { expect } = require('aegir/utils/chai') -const { Multiaddr } = require('multiaddr') -const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') - -const mafmt = require('./../src') +import { expect } from 'aegir/utils/chai.js' +import { Multiaddr } from '@multiformats/multiaddr' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import * as mafmt from '../src/index.js' +import type { Mafmt } from '../src/index.js' describe('multiaddr validation', function () { const goodDNS = [ @@ -190,11 +187,7 @@ describe('multiaddr validation', function () { '/dns6/nyc-2.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64' ].concat(goodCircuit) - /** - * @param {import('../').Mafmt} p - * @param {...string[]} tests - */ - function assertMatches (p, ...tests) { + function assertMatches (p: Mafmt, ...tests: string[][]) { tests.forEach(function (test) { test.forEach(function (testcase) { try { @@ -202,19 +195,15 @@ describe('multiaddr validation', function () { const ma = new Multiaddr(testcase) expect(p.matches(ma), `assertMatches: ${testcase} (multiaddr object)`).to.be.eql(true) expect(p.matches(ma.bytes), `assertMatches: ${testcase} (multiaddr.bytes)`).to.be.eql(true) - } catch (err) { - err.stack = '[testcase=' + JSON.stringify(testcase) + ', shouldMatch=true] ' + err.stack + } catch (err: any) { + err.stack = `[testcase=${JSON.stringify(testcase)}, shouldMatch=true] ${err.stack}` // eslint-disable-line @typescript-eslint/restrict-template-expressions throw err } }) }) } - /** - * @param {import('../').Mafmt} p - * @param {...string[]} tests - */ - function assertMismatches (p, ...tests) { + function assertMismatches (p: Mafmt, ...tests: string[][]) { tests.forEach(function (test) { test.forEach(function (testcase) { try { @@ -228,12 +217,12 @@ describe('multiaddr validation', function () { // Ignoring testcase as the string is not a multiaddr // (There is a separate 'Uint8Array is invalid' test later below) } - if (validMultiaddrObj) { + if (validMultiaddrObj != null) { expect(p.matches(validMultiaddrObj), `assertMismatches: ${testcase} (multiaddr object)`).to.be.eql(false) expect(p.matches(validMultiaddrObj.bytes), `assertMismatches: ${testcase} (multiaddr.bytes)`).to.be.eql(false) } - } catch (err) { - err.stack = '[testcase=' + JSON.stringify(testcase) + ', shouldMatch=false] ' + err.stack + } catch (err: any) { + err.stack = `[testcase=${JSON.stringify(testcase)}, shouldMatch=false] ${err.stack}` // eslint-disable-line @typescript-eslint/restrict-template-expressions throw err } }) diff --git a/tsconfig.json b/tsconfig.json index 05bfbfd..f296f99 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,12 @@ { - "extends": "aegir/src/config/tsconfig.aegir.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": [ - "src", - "test" - ] - } + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist", + "emitDeclarationOnly": false, + "module": "ES2020" + }, + "include": [ + "src", + "test" + ] +}