diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 25fc395..c9772b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: ci +name: test & maybe release on: push: branches: @@ -8,67 +8,118 @@ on: - master jobs: + check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - run: npm install - - run: npx aegir lint - - run: npx aegir ts -p check - - run: npx aegir build - - run: npx aegir dep-check - - uses: ipfs/aegir/actions/bundle-size@master + - uses: actions/setup-node@v2 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + 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: [14, 15] + node: [16] fail-fast: true steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node }} - - run: npm install - - run: npx nyc --reporter=lcov aegir test -t node -- --bail + - 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 - - run: npm install - - run: npx aegir test -t browser -t webworker --bail + - 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 - - run: npm install - - run: npx aegir test -t browser -t webworker --bail -- --browsers FirefoxHeadless - test-webkit: + - 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: microsoft/playwright-github-action@v1 - - run: npm install - - run: npx aegir test -t browser -t webworker --bail -- --browser webkit + - 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 - - run: npm install - - run: npx xvfb-maybe aegir test -t electron-main --bail + - 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 - - run: npm install - - run: npx xvfb-maybe aegir test -t electron-renderer --bail \ No newline at end of file + - 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/LICENSE b/LICENSE index 60d5000..b0b237f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,2 @@ -The MIT License (MIT) - -Copyright (c) IPFS - -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. +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 19197a9..564fa57 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ > This package serves as a central repository for shared logic and dependencies for all libp2p packages, using `libp2p-utils` helps to easily re-use small scoped blocks of logic across all libp2p modules and also as a dependency proxy (think `aegir` for domain logic dependencies). -The libp2p ecosystem has lots of repos with it comes several problems like: +The libp2p ecosystem has lots of repos with it comes several problems like: - Domain logic dedupe - all modules shared a lot of logic like validation, streams handling, etc. - Dependencies management - it's really easy with so many repos for dependencies to go out of control, they become outdated, different repos use different modules to do the same thing (like merging defaults options), browser bundles ends up with multiple versions of the same package, bumping versions is cumbersome to do because we need to go through several repos, etc. @@ -26,14 +26,14 @@ These problems are the motivation for this package, having shared logic in this ```bash -$ npm install --save libp2p-utils +$ npm install --save @libp2p/utils ``` ## Usage Each function should be imported directly. ```js -const ipAndPortToMultiaddr = require('libp2p-utils/src/ip-port-to-multiaddr') +import ipAndPortToMultiaddr from '@libp2p/utils/ip-port-to-multiaddr' const ma = ipAndPortToMultiaddr('127.0.0.1', 9000) ``` diff --git a/package.json b/package.json index 7cb0c7a..397aad9 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,35 @@ { - "name": "libp2p-utils", + "name": "@libp2p/utils", "version": "0.4.1", "description": "Package to aggregate shared logic and dependencies for the libp2p ecosystem", "leadMaintainer": "Vasco Santos ", - "main": "src/index.js", - "types": "dist/src/index.d.ts", + "type": "module", + "exports": { + "multiaddr/is-loopback": { + "import": "./dist/src/multiaddr/is-loopback.js", + "types": "./dist/src/multiaddr/is-loopback.d.ts" + }, + "multiaddr/is-private": { + "import": "./dist/src/multiaddr/is-private.js", + "types": "./dist/src/multiaddr/is-private.d.ts" + }, + "address-sort": { + "import": "./dist/src/address-sort.js", + "types": "./dist/src/address-sort.d.ts" + }, + "array-equals": { + "import": "./dist/src/array-equals.js", + "types": "./dist/src/array-equals.d.ts" + }, + "ip-port-to-multiaddr": { + "import": "./dist/src/ip-port-to-multiaddr.js", + "types": "./dist/src/ip-port-to-multiaddr.d.ts" + }, + "stream-to-ma-conn": { + "import": "./dist/src/stream-to-ma-connr.js", + "types": "./dist/src/stream-to-ma-connr.d.ts" + } + }, "typesVersions": { "*": { "src/*": [ @@ -15,49 +40,47 @@ }, "files": [ "src", - "dist" + "dist/src" ], "scripts": { - "prepare": "aegir build --no-bundle", - "test": "aegir test", - "test:browser": "aegir test -t browser", - "test:node": "aegir test -t node", - "test:electron": "aegir test -t electron-main", - "test:electron-renderer": "aegir test -t electron-renderer", "lint": "aegir lint", - "release": "aegir release --docs", - "release-minor": "aegir release --target node --type minor --docs", - "release-major": "aegir release --type major --docs", - "build": "aegir build" + "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" }, - "pre-push": [ - "lint" - ], "repository": { "type": "git", "url": "git+https://github.com/libp2p/js-libp2p-utils.git" }, - "license": "MIT", + "license": "(Apache-2.0 OR MIT)", "bugs": { "url": "https://github.com/libp2p/js-libp2p-utils/issues" }, "homepage": "https://github.com/libp2p/js-libp2p-utils#readme", "devDependencies": { + "@libp2p/interfaces": "^0.2.0", "@types/debug": "^4.1.5", - "aegir": "^33.0.0", + "aegir": "^36.1.2", "it-pair": "^1.0.0", "it-pipe": "^1.1.0", - "libp2p-interfaces": "^1.0.0", "streaming-iterables": "^6.0.0", "util": "^0.12.3" }, "dependencies": { + "@achingbrain/ip-address": "^8.1.0", + "@multiformats/multiaddr": "^10.1.1", "abortable-iterator": "^3.0.0", "debug": "^4.3.0", "err-code": "^3.0.1", - "ip-address": "^8.0.0", "is-loopback-addr": "^1.0.0", - "multiaddr": "^10.0.0", "private-ip": "^2.1.1" }, "contributors": [ diff --git a/src/address-sort.js b/src/address-sort.ts similarity index 72% rename from src/address-sort.js rename to src/address-sort.ts index 2e54de8..7459142 100644 --- a/src/address-sort.js +++ b/src/address-sort.ts @@ -1,10 +1,10 @@ -'use strict' +import type { Multiaddr } from '@multiformats/multiaddr' +import { isPrivate } from './multiaddr/is-private.js' -const isPrivate = require('./multiaddr/is-private') - -/** - * @typedef {import('multiaddr').Multiaddr} Multiaddr - */ +interface Address { + multiaddr: Multiaddr + isCertified: boolean +} /** * @typedef {Object} Address @@ -21,7 +21,7 @@ const isPrivate = require('./multiaddr/is-private') * @param {Address} b * @returns {number} */ -function addressesPublicFirstCompareFunction (a, b) { +function addressesPublicFirstCompareFunction (a: Address, b: Address) { const isAPrivate = isPrivate(a.multiaddr) const isBPrivate = isPrivate(b.multiaddr) @@ -43,12 +43,7 @@ function addressesPublicFirstCompareFunction (a, b) { /** * Sort given addresses by putting public addresses first. * In case of equality, a certified address will come first. - * - * @param {Array
} addresses - * @returns {Array
} */ -function publicAddressesFirst (addresses) { +export function publicAddressesFirst (addresses: Address[]) { return [...addresses].sort(addressesPublicFirstCompareFunction) } - -module.exports.publicAddressesFirst = publicAddressesFirst diff --git a/src/array-equals.js b/src/array-equals.js deleted file mode 100644 index bb9f91c..0000000 --- a/src/array-equals.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -/** - * Verify if two arrays of non primitive types with the "equals" function are equal. - * Compatible with multiaddr, peer-id and others. - * - * @param {Array<*>} a - * @param {Array<*>} b - * @returns {boolean} - */ -function arrayEquals (a, b) { - return a.length === b.length && b.sort() && a.sort().every((item, index) => b[index].equals(item)) -} - -module.exports = arrayEquals diff --git a/src/array-equals.ts b/src/array-equals.ts new file mode 100644 index 0000000..7455201 --- /dev/null +++ b/src/array-equals.ts @@ -0,0 +1,8 @@ +/** + * Verify if two arrays of non primitive types with the "equals" function are equal. + * Compatible with multiaddr, peer-id and others. + */ +export function arrayEquals (a: any[], b: any[]) { + const sort = (a: any, b: any) => a.toString().localeCompare(b.toString()) + return a.length === b.length && b.sort(sort) && a.sort(sort).every((item, index) => b[index].equals(item)) +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/ip-port-to-multiaddr.js b/src/ip-port-to-multiaddr.ts similarity index 62% rename from src/ip-port-to-multiaddr.js rename to src/ip-port-to-multiaddr.ts index 00b9f92..1bca51f 100644 --- a/src/ip-port-to-multiaddr.js +++ b/src/ip-port-to-multiaddr.ts @@ -1,14 +1,13 @@ -'use strict' +import debug from 'debug' +import { Multiaddr } from '@multiformats/multiaddr' +import errCode from 'err-code' +import { Address4, Address6 } from '@achingbrain/ip-address' -const debug = require('debug') const log = Object.assign(debug('libp2p:ip-port-to-multiaddr'), { error: debug('libp2p:ip-port-to-multiaddr:err') }) -const { Multiaddr } = require('multiaddr') -const errCode = require('err-code') -const { Address4, Address6 } = require('ip-address') -const errors = { +export const Errors = { ERR_INVALID_IP_PARAMETER: 'ERR_INVALID_IP_PARAMETER', ERR_INVALID_PORT_PARAMETER: 'ERR_INVALID_PORT_PARAMETER', ERR_INVALID_IP: 'ERR_INVALID_IP' @@ -16,13 +15,10 @@ const errors = { /** * Transform an IP, Port pair into a multiaddr - * - * @param {string} ip - * @param {number|string} port */ -function ipPortToMultiaddr (ip, port) { +export function ipPortToMultiaddr (ip: string, port: number | string) { if (typeof ip !== 'string') { - throw errCode(new Error(`invalid ip provided: ${ip}`), errors.ERR_INVALID_IP_PARAMETER) + throw errCode(new Error(`invalid ip provided: ${ip}`), Errors.ERR_INVALID_IP_PARAMETER) // eslint-disable-line @typescript-eslint/restrict-template-expressions } if (typeof port === 'string') { @@ -30,7 +26,7 @@ function ipPortToMultiaddr (ip, port) { } if (isNaN(port)) { - throw errCode(new Error(`invalid port provided: ${port}`), errors.ERR_INVALID_PORT_PARAMETER) + throw errCode(new Error(`invalid port provided: ${port}`), Errors.ERR_INVALID_PORT_PARAMETER) } try { @@ -48,10 +44,6 @@ function ipPortToMultiaddr (ip, port) { } catch (err) { const errMsg = `invalid ip:port for creating a multiaddr: ${ip}:${port}` log.error(errMsg) - throw errCode(new Error(errMsg), errors.ERR_INVALID_IP) + throw errCode(new Error(errMsg), Errors.ERR_INVALID_IP) } } - -module.exports = ipPortToMultiaddr - -module.exports.Errors = errors diff --git a/src/multiaddr/is-loopback.js b/src/multiaddr/is-loopback.js deleted file mode 100644 index dd29c3d..0000000 --- a/src/multiaddr/is-loopback.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -// @ts-ignore is-loopback-addr does not publish types -const isLoopbackAddr = require('is-loopback-addr') - -/** - * @typedef {import('multiaddr').Multiaddr} Multiaddr - */ - -/** - * Check if a given multiaddr is a loopback address. - * - * @param {Multiaddr} ma - * @returns {boolean} - */ -function isLoopback (ma) { - const { address } = ma.nodeAddress() - - return isLoopbackAddr(address) -} - -module.exports = isLoopback diff --git a/src/multiaddr/is-loopback.ts b/src/multiaddr/is-loopback.ts new file mode 100644 index 0000000..aae4e20 --- /dev/null +++ b/src/multiaddr/is-loopback.ts @@ -0,0 +1,12 @@ +// @ts-expect-error is-loopback-addr does not publish types +import isLoopbackAddr from 'is-loopback-addr' +import type { Multiaddr } from '@multiformats/multiaddr' + +/** + * Check if a given multiaddr is a loopback address. + */ +export function isLoopback (ma: Multiaddr) { + const { address } = ma.nodeAddress() + + return isLoopbackAddr(address) +} diff --git a/src/multiaddr/is-private.js b/src/multiaddr/is-private.js deleted file mode 100644 index efe6b2f..0000000 --- a/src/multiaddr/is-private.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -// @ts-ignore private-ip does not publish types -const isIpPrivate = require('private-ip') - -/** - * @typedef {import('multiaddr').Multiaddr} Multiaddr - */ - -/** - * Check if a given multiaddr has a private address. - * - * @param {Multiaddr} ma - * @returns {boolean} - */ -function isPrivate (ma) { - const { address } = ma.nodeAddress() - - return isIpPrivate(address) -} - -module.exports = isPrivate diff --git a/src/multiaddr/is-private.ts b/src/multiaddr/is-private.ts new file mode 100644 index 0000000..54e8072 --- /dev/null +++ b/src/multiaddr/is-private.ts @@ -0,0 +1,11 @@ +import type { Multiaddr } from '@multiformats/multiaddr' +import isIpPrivate from 'private-ip' + +/** + * Check if a given multiaddr has a private address. + */ +export function isPrivate (ma: Multiaddr) { + const { address } = ma.nodeAddress() + + return Boolean(isIpPrivate(address)) +} diff --git a/src/stream-to-ma-conn.js b/src/stream-to-ma-conn.js deleted file mode 100644 index eb9c0ab..0000000 --- a/src/stream-to-ma-conn.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict' - -const { source: abortable } = require('abortable-iterator') -const debug = require('debug') -const log = debug('libp2p:stream:converter') - -/** - * @typedef {import('multiaddr').Multiaddr} Multiaddr - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - * - * @typedef {Object} Timeline - * @property {number} open - connection opening timestamp. - * @property {number} [upgraded] - connection upgraded timestamp. - * @property {number} [close] - */ - -/** - * Convert a duplex iterable into a MultiaddrConnection. - * https://github.com/libp2p/interface-transport#multiaddrconnection - * - * @param {object} streamProperties - * @param {MuxedStream} streamProperties.stream - * @param {Multiaddr} streamProperties.remoteAddr - * @param {Multiaddr} streamProperties.localAddr - * @param {object} [options] - * @param {AbortSignal} [options.signal] - * @returns {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} - */ -function streamToMaConnection ({ stream, remoteAddr, localAddr }, options = {}) { - const { sink, source } = stream - const maConn = { - /** - * @param {Uint8Array} source - */ - async sink (source) { - if (options.signal) { - // @ts-ignore ts infers source template will be a number - source = abortable(source, options.signal) - } - - try { - await sink(source) - } catch (err) { - // If aborted we can safely ignore - if (err.type !== 'aborted') { - // If the source errored the socket will already have been destroyed by - // toIterable.duplex(). If the socket errored it will already be - // destroyed. There's nothing to do here except log the error & return. - log(err) - } - } - close() - }, - source: options.signal ? abortable(source, options.signal) : source, - conn: stream, - localAddr, - remoteAddr, - /** @type {Timeline} */ - timeline: { open: Date.now(), close: undefined }, - close () { - sink(new Uint8Array(0)) - return close() - } - } - - function close () { - if (!maConn.timeline.close) { - maConn.timeline.close = Date.now() - } - return Promise.resolve() - } - - return maConn -} - -module.exports = streamToMaConnection diff --git a/src/stream-to-ma-conn.ts b/src/stream-to-ma-conn.ts new file mode 100644 index 0000000..3be9b52 --- /dev/null +++ b/src/stream-to-ma-conn.ts @@ -0,0 +1,92 @@ +import { source as abortable } from 'abortable-iterator' +import debug from 'debug' +import type { MuxedStream } from '@libp2p/interfaces/stream-muxer' +import type { Multiaddr } from '@multiformats/multiaddr' +import type { MultiaddrConnection } from '@libp2p/interfaces/transport' + +const log = debug('libp2p:stream:converter') + +/** + * @typedef {Object} Timeline + * @property {number} open - + * @property {number} [upgraded] - . + * @property {number} [close] + */ + +export interface Timeline { + /** + * Connection opening timestamp + */ + open: number + + /** + * Connection upgraded timestamp + */ + upgraded?: number + + /** + * Connection closed timestamp + */ + close?: number +} + +interface StreamOptions { + signal?: AbortSignal + +} + +interface StreamProperties { + stream: MuxedStream + remoteAddr: Multiaddr + localAddr: Multiaddr +} + +/** + * Convert a duplex iterable into a MultiaddrConnection. + * https://github.com/libp2p/interface-transport#multiaddrconnection + */ +export function streamToMaConnection (props: StreamProperties, options: StreamOptions = {}) { + const { stream, remoteAddr, localAddr } = props + const { sink, source } = stream + const maConn: MultiaddrConnection = { + async sink (source) { + if (options.signal != null) { + source = abortable(source, options.signal) + } + + try { + await sink(source) + await close() + } catch (err: any) { + // If aborted we can safely ignore + if (err.type !== 'aborted') { + // If the source errored the socket will already have been destroyed by + // toIterable.duplex(). If the socket errored it will already be + // destroyed. There's nothing to do here except log the error & return. + log(err) + } + } + }, + source: (options.signal != null) ? abortable(source, options.signal) : source, + conn: stream, + localAddr, + remoteAddr, + /** @type {Timeline} */ + timeline: { open: Date.now(), close: undefined }, + async close () { + await sink(async function * () { + yield new Uint8Array(0) + }()) + await close() + } + } + + async function close () { + if (maConn.timeline.close == null) { + maConn.timeline.close = Date.now() + } + return await Promise.resolve() + } + + return maConn +} diff --git a/test/address-sort.spec.js b/test/address-sort.spec.ts similarity index 90% rename from test/address-sort.spec.js rename to test/address-sort.spec.ts index 4f1780e..06bdeff 100644 --- a/test/address-sort.spec.js +++ b/test/address-sort.spec.ts @@ -1,10 +1,8 @@ -'use strict' /* eslint-env mocha */ -const { expect } = require('aegir/utils/chai') -const { Multiaddr } = require('multiaddr') - -const { publicAddressesFirst } = require('../src/address-sort') +import { expect } from 'aegir/utils/chai.js' +import { Multiaddr } from '@multiformats/multiaddr' +import { publicAddressesFirst } from '../src/address-sort.js' describe('address-sort', () => { it('should sort public addresses first', () => { diff --git a/test/array-equals.spec.js b/test/array-equals.spec.ts similarity index 92% rename from test/array-equals.spec.js rename to test/array-equals.spec.ts index bb70cba..6aa3977 100644 --- a/test/array-equals.spec.js +++ b/test/array-equals.spec.ts @@ -1,10 +1,8 @@ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const { Multiaddr } = require('multiaddr') - -const arrayEquals = require('../src/array-equals') +import { expect } from 'aegir/utils/chai.js' +import { Multiaddr } from '@multiformats/multiaddr' +import { arrayEquals } from '../src/array-equals.js' describe('non primitive array equals', () => { it('returns true if two arrays of multiaddrs are equal', () => { diff --git a/test/ip-port-to-multiaddr.spec.js b/test/ip-port-to-multiaddr.spec.js deleted file mode 100644 index e51aeb0..0000000 --- a/test/ip-port-to-multiaddr.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const { expect } = require('aegir/utils/chai') -const toMultiaddr = require('../src/ip-port-to-multiaddr') -const { Errors } = require('../src/ip-port-to-multiaddr') - -describe('IP and port to Multiaddr', () => { - it('creates multiaddr from valid IPv4 IP and port', () => { - const ip = '127.0.0.1' - const port = '9090' - expect(toMultiaddr(ip, port).toString()).to.equal(`/ip4/${ip}/tcp/${port}`) - }) - - it('creates multiaddr from valid IPv4 IP and numeric port', () => { - const ip = '127.0.0.1' - const port = 9090 - expect(toMultiaddr(ip, port).toString()).to.equal(`/ip4/${ip}/tcp/${port}`) - }) - - it('creates multiaddr from valid IPv4 in IPv6 IP and port', () => { - const ip = '0:0:0:0:0:0:101.45.75.219' - const port = '9090' - expect(toMultiaddr(ip, port).toString()).to.equal(`/ip4/101.45.75.219/tcp/${port}`) - }) - - it('creates multiaddr from valid IPv6 IP and port', () => { - const ip = '::1' - const port = '9090' - expect(toMultiaddr(ip, port).toString()).to.equal(`/ip6/${ip}/tcp/${port}`) - }) - - it('throws for missing IP address', () => { - expect(() => toMultiaddr()).to.throw('invalid ip provided').with.property('code', Errors.ERR_INVALID_IP_PARAMETER) - }) - - it('throws for invalid IP address', () => { - const ip = 'aewmrn4awoew' - const port = '234' - expect(() => toMultiaddr(ip, port)).to.throw('invalid ip:port for creating a multiaddr').with.property('code', Errors.ERR_INVALID_IP) - }) - - it('throws for invalid port', () => { - const ip = '127.0.0.1' - const port = 'garbage' - expect(() => toMultiaddr(ip, port)).to.throw('invalid port provided').with.property('code', Errors.ERR_INVALID_PORT_PARAMETER) - }) -}) diff --git a/test/ip-port-to-multiaddr.spec.ts b/test/ip-port-to-multiaddr.spec.ts new file mode 100644 index 0000000..39e5897 --- /dev/null +++ b/test/ip-port-to-multiaddr.spec.ts @@ -0,0 +1,47 @@ +/* eslint-env mocha */ + +import { expect } from 'aegir/utils/chai.js' +import { ipPortToMultiaddr, Errors } from '../src/ip-port-to-multiaddr.js' + +describe('IP and port to Multiaddr', () => { + it('creates multiaddr from valid IPv4 IP and port', () => { + const ip = '127.0.0.1' + const port = '9090' + expect(ipPortToMultiaddr(ip, port).toString()).to.equal(`/ip4/${ip}/tcp/${port}`) + }) + + it('creates multiaddr from valid IPv4 IP and numeric port', () => { + const ip = '127.0.0.1' + const port = 9090 + expect(ipPortToMultiaddr(ip, port).toString()).to.equal(`/ip4/${ip}/tcp/${port}`) + }) + + it('creates multiaddr from valid IPv4 in IPv6 IP and port', () => { + const ip = '0:0:0:0:0:0:101.45.75.219' + const port = '9090' + expect(ipPortToMultiaddr(ip, port).toString()).to.equal(`/ip4/101.45.75.219/tcp/${port}`) + }) + + it('creates multiaddr from valid IPv6 IP and port', () => { + const ip = '::1' + const port = '9090' + expect(ipPortToMultiaddr(ip, port).toString()).to.equal(`/ip6/${ip}/tcp/${port}`) + }) + + it('throws for missing IP address', () => { + // @ts-expect-error invalid args + expect(() => ipPortToMultiaddr()).to.throw('invalid ip provided').with.property('code', Errors.ERR_INVALID_IP_PARAMETER) + }) + + it('throws for invalid IP address', () => { + const ip = 'aewmrn4awoew' + const port = '234' + expect(() => ipPortToMultiaddr(ip, port)).to.throw('invalid ip:port for creating a multiaddr').with.property('code', Errors.ERR_INVALID_IP) + }) + + it('throws for invalid port', () => { + const ip = '127.0.0.1' + const port = 'garbage' + expect(() => ipPortToMultiaddr(ip, port)).to.throw('invalid port provided').with.property('code', Errors.ERR_INVALID_PORT_PARAMETER) + }) +}) diff --git a/test/multiaddr/is-loopback.spec.js b/test/multiaddr/is-loopback.spec.ts similarity index 89% rename from test/multiaddr/is-loopback.spec.js rename to test/multiaddr/is-loopback.spec.ts index 9c59157..4431962 100644 --- a/test/multiaddr/is-loopback.spec.js +++ b/test/multiaddr/is-loopback.spec.ts @@ -1,10 +1,8 @@ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const { Multiaddr } = require('multiaddr') - -const isLoopback = require('../../src/multiaddr/is-loopback') +import { expect } from 'aegir/utils/chai.js' +import { Multiaddr } from '@multiformats/multiaddr' +import { isLoopback } from '../../src/multiaddr/is-loopback.js' describe('multiaddr isLoopback', () => { it('identifies loopback ip4 multiaddrs', () => { diff --git a/test/multiaddr/is-private.spec.js b/test/multiaddr/is-private.spec.ts similarity index 86% rename from test/multiaddr/is-private.spec.js rename to test/multiaddr/is-private.spec.ts index 0c9418b..dcfe813 100644 --- a/test/multiaddr/is-private.spec.js +++ b/test/multiaddr/is-private.spec.ts @@ -1,10 +1,8 @@ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const { Multiaddr } = require('multiaddr') - -const isPrivate = require('../../src/multiaddr/is-private') +import { expect } from 'aegir/utils/chai.js' +import { Multiaddr } from '@multiformats/multiaddr' +import { isPrivate } from '../../src/multiaddr/is-private.js' describe('multiaddr isPrivate', () => { it('identifies private ip4 multiaddrs', () => { @@ -47,7 +45,7 @@ describe('multiaddr isPrivate', () => { }) }) - it('identifies other multiaddrs as not private addresses', () => { + it('identifies other multiaddrs as not private addresses', () => { [ new Multiaddr('/dns4/wss0.bootstrap.libp2p.io/tcp/443'), new Multiaddr('/dns6/wss0.bootstrap.libp2p.io/tcp/443') diff --git a/test/stream-to-ma-conn.spec.js b/test/stream-to-ma-conn.spec.ts similarity index 57% rename from test/stream-to-ma-conn.spec.js rename to test/stream-to-ma-conn.spec.ts index e7d322a..ee1ba96 100644 --- a/test/stream-to-ma-conn.spec.js +++ b/test/stream-to-ma-conn.spec.ts @@ -1,21 +1,21 @@ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const pair = require('it-pair') -const pipe = require('it-pipe') -const { collect } = require('streaming-iterables') -const { Multiaddr } = require('multiaddr') - -const streamToMaConn = require('../src/stream-to-ma-conn') +import { expect } from 'aegir/utils/chai.js' +// @ts-expect-error no types +import pair from 'it-pair' +import pipe from 'it-pipe' +import { collect } from 'streaming-iterables' +import { Multiaddr } from '@multiformats/multiaddr' +import { streamToMaConnection } from '../src/stream-to-ma-conn.js' describe('Convert stream into a multiaddr connection', () => { - it('converts a stream and adds the provided metadata', () => { + const localAddr = new Multiaddr('/ip4/101.45.75.219/tcp/6000') + const remoteAddr = new Multiaddr('/ip4/100.46.74.201/tcp/6002') + + it('converts a stream and adds the provided metadata', async () => { const stream = pair() - const localAddr = new Multiaddr('/ip4/101.45.75.219/tcp/6000') - const remoteAddr = new Multiaddr('/ip4/100.46.74.201/tcp/6002') - const maConn = streamToMaConn({ + const maConn = streamToMaConnection({ stream, localAddr, remoteAddr @@ -30,13 +30,17 @@ describe('Convert stream into a multiaddr connection', () => { expect(maConn.timeline.open).to.exist() expect(maConn.timeline.close).to.not.exist() - maConn.close() + await maConn.close() expect(maConn.timeline.close).to.exist() }) it('can stream data over the multiaddr connection', async () => { const stream = pair() - const maConn = streamToMaConn({ stream }) + const maConn = streamToMaConnection({ + stream, + localAddr, + remoteAddr + }) const data = 'hey' const streamData = await pipe( diff --git a/tsconfig.json b/tsconfig.json index 5fe8ea4..f296f99 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,12 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist", + "emitDeclarationOnly": false, + "module": "ES2020" }, "include": [ - "src" + "src", + "test" ] }