diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml index 5e2410d..d713947 100644 --- a/.github/workflows/js-test-and-release.yml +++ b/.github/workflows/js-test-and-release.yml @@ -1,181 +1,21 @@ name: test & maybe release + on: push: branches: - master pull_request: + workflow_dispatch: -jobs: - - check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - 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: [lts/*] - fail-fast: true - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:node - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: node - - test-chrome: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:chrome - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: chrome - - test-chrome-webworker: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:chrome-webworker - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: chrome-webworker - - test-firefox: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:firefox - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: firefox +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true - test-firefox-webworker: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:firefox-webworker - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: firefox-webworker - - test-webkit: - needs: check - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - node: [lts/*] - fail-fast: true - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:webkit - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: webkit - - test-webkit-webworker: - needs: check - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - node: [lts/*] - fail-fast: true - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:webkit-webworker - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: webkit-webworker - - test-electron-main: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npx xvfb-maybe npm run --if-present test:electron-main - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: electron-main - - test-electron-renderer: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npx xvfb-maybe npm run --if-present test:electron-renderer - - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 - with: - flags: electron-renderer - - release: - needs: [test-node, test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-webkit, test-webkit-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@v3 - with: - fetch-depth: 0 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - uses: ipfs/aegir/actions/docker-login@master - with: - docker-token: ${{ secrets.DOCKER_TOKEN }} - docker-username: ${{ secrets.DOCKER_USERNAME }} - - run: npm run --if-present release - env: - GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN || github.token }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} +jobs: + js-test-and-release: + uses: pl-strflt/uci/.github/workflows/js-test-and-release.yml@v0.0 + secrets: + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + UCI_GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN }} diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml new file mode 100644 index 0000000..bd00f09 --- /dev/null +++ b/.github/workflows/semantic-pull-request.yml @@ -0,0 +1,12 @@ +name: Semantic PR + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + uses: pl-strflt/.github/.github/workflows/reusable-semantic-pull-request.yml@v0.3 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..16d65d7 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,13 @@ +name: Close and mark stale issue + +on: + schedule: + - cron: '0 0 * * *' + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3 diff --git a/README.md b/README.md index d5d9f4b..511f613 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,15 @@ -# @chainsafe/libp2p-yamux - [![codecov](https://img.shields.io/codecov/c/github/ChainSafe/js-libp2p-yamux.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/js-libp2p-yamux) [![CI](https://img.shields.io/github/actions/workflow/status/ChainSafe/js-libp2p-yamux/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/ChainSafe/js-libp2p-yamux/actions/workflows/js-test-and-release.yml?query=branch%3Amaster) > Yamux stream multiplexer for libp2p -## Table of contents - -- [Install](#install) - - [Browser ` ``` -## Usage - -```js -import { yamux } from '@chainsafe/libp2p-yamux' -import { pipe } from 'it-pipe' -import { duplexPair } from 'it-pair/duplex' -import all from 'it-all' - -// Connect two yamux muxers to demo basic stream multiplexing functionality - -const clientMuxer = yamux({ - client: true, - onIncomingStream: stream => { - // echo data on incoming streams - pipe(stream, stream) - }, - onStreamEnd: stream => { - // do nothing - } -})() - -const serverMuxer = yamux({ - client: false, - onIncomingStream: stream => { - // echo data on incoming streams - pipe(stream, stream) - }, - onStreamEnd: stream => { - // do nothing - } -})() - -// `p` is our "connections", what we use to connect the two sides -// In a real application, a connection is usually to a remote computer -const p = duplexPair() - -// connect the muxers together -pipe(p[0], clientMuxer, p[0]) -pipe(p[1], serverMuxer, p[1]) - -// now either side can open streams -const stream0 = clientMuxer.newStream() -const stream1 = serverMuxer.newStream() - -// Send some data to the other side -const encoder = new TextEncoder() -const data = [encoder.encode('hello'), encoder.encode('world')] -pipe(data, stream0) - -// Receive data back -const result = await pipe(stream0, all) - -// close a stream -stream1.close() - -// close the muxer -clientMuxer.close() -``` - -## API - -This library implements the `StreamMuxerFactory`, `StreamMuxer` and `Stream` interfaces defined in [`@libp2p/interfaces/stream-muxer`](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/stream-muxer). - -## Contribute - -The libp2p implementation in JavaScript is a work in progress. As such, there are a few things you can do right now to help out: - -- Go through the modules and **check out existing issues**. This is especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically. -- **Perform code reviews**. More eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs. - -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/package.json b/package.json index 571e045..7334ece 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,6 @@ "muxer", "stream" ], - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" - }, "type": "module", "types": "./dist/src/index.d.ts", "typesVersions": { @@ -63,6 +59,7 @@ "eslintConfig": { "extends": "ipfs", "parserOptions": { + "project": true, "sourceType": "module" }, "ignorePatterns": [ @@ -183,7 +180,7 @@ "@dapplion/benchmark": "^0.2.4", "@libp2p/interface-compliance-tests": "^4.0.0", "@libp2p/mplex": "^9.0.0", - "aegir": "^40.0.1", + "aegir": "^41.1.10", "it-drain": "^3.0.2", "it-pair": "^2.0.6", "it-stream-types": "^2.0.1" diff --git a/src/decode.ts b/src/decode.ts index 8433f8f..36b4bbe 100644 --- a/src/decode.ts +++ b/src/decode.ts @@ -55,7 +55,7 @@ export class Decoder { * Note: If `readData` is emitted, it _must_ be called before the next iteration * Otherwise an error is thrown */ - async * emitFrames (): AsyncGenerator<{ header: FrameHeader, readData?: () => Promise }> { + async * emitFrames (): AsyncGenerator<{ header: FrameHeader, readData?(): Promise }> { for await (const chunk of this.source) { this.buffer.append(chunk) diff --git a/src/index.ts b/src/index.ts index ade3ad8..401e8e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,88 @@ +/** + * @packageDocumentation + * + * This module is a JavaScript implementation of [Yamux from Hashicorp](https://github.com/hashicorp/yamux/blob/master/spec.md) designed to be used with [js-libp2p](https://github.com/libp2p/js-libp2p). + * + * @example Configure libp2p with Yamux + * + * ```typescript + * import { createLibp2p } from 'libp2p' + * import { yamux } from '@chainsafe/libp2p-yamux' + * + * const node = await createLibp2p({ + * // ... other options + * streamMuxers: [ + * yamux() + * ] + * }) + * ``` + * + * @example Using the low-level API + * + * ```js + * import { yamux } from '@chainsafe/libp2p-yamux' + * import { pipe } from 'it-pipe' + * import { duplexPair } from 'it-pair/duplex' + * import all from 'it-all' + * + * // Connect two yamux muxers to demo basic stream multiplexing functionality + * + * const clientMuxer = yamux({ + * client: true, + * onIncomingStream: stream => { + * // echo data on incoming streams + * pipe(stream, stream) + * }, + * onStreamEnd: stream => { + * // do nothing + * } + * })() + * + * const serverMuxer = yamux({ + * client: false, + * onIncomingStream: stream => { + * // echo data on incoming streams + * pipe(stream, stream) + * }, + * onStreamEnd: stream => { + * // do nothing + * } + * })() + * + * // `p` is our "connections", what we use to connect the two sides + * // In a real application, a connection is usually to a remote computer + * const p = duplexPair() + * + * // connect the muxers together + * pipe(p[0], clientMuxer, p[0]) + * pipe(p[1], serverMuxer, p[1]) + * + * // now either side can open streams + * const stream0 = clientMuxer.newStream() + * const stream1 = serverMuxer.newStream() + * + * // Send some data to the other side + * const encoder = new TextEncoder() + * const data = [encoder.encode('hello'), encoder.encode('world')] + * pipe(data, stream0) + * + * // Receive data back + * const result = await pipe(stream0, all) + * + * // close a stream + * stream1.close() + * + * // close the muxer + * clientMuxer.close() + * ``` + */ + import { Yamux } from './muxer.js' import type { YamuxMuxerInit } from './muxer.js' import type { StreamMuxerFactory } from '@libp2p/interface/stream-muxer' -export { GoAwayCode } from './frame.js' + +export { GoAwayCode, type FrameHeader, type FrameType } from './frame.js' +export type { YamuxMuxerInit } export function yamux (init: YamuxMuxerInit = {}): () => StreamMuxerFactory { return () => new Yamux(init) diff --git a/src/muxer.ts b/src/muxer.ts index cf0c012..f409016 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -60,7 +60,7 @@ export class YamuxMuxer implements StreamMuxer { /** The next ping id to be used when pinging */ private nextPingID: number /** Tracking info for the currently active ping */ - private activePing?: { id: number, promise: Promise, resolve: () => void } + private activePing?: { id: number, promise: Promise, resolve(): void } /** Round trip time */ private rtt: number diff --git a/src/stream.ts b/src/stream.ts index 5e7df08..6223072 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -17,8 +17,8 @@ export enum StreamState { export interface YamuxStreamInit extends AbstractStreamInit { name?: string - sendFrame: (header: FrameHeader, body?: Uint8Array) => void - getRTT: () => number + sendFrame(header: FrameHeader, body?: Uint8Array): void + getRTT(): number config: Config state: StreamState } diff --git a/test/mplex.util.ts b/test/mplex.util.ts index 12ceeed..26a2478 100644 --- a/test/mplex.util.ts +++ b/test/mplex.util.ts @@ -16,7 +16,7 @@ export function testYamuxMuxer (name: string, client: boolean, conf: StreamMuxer /** * Create a transform that can be paused and unpaused */ -export function pauseableTransform (): { transform: Transform, AsyncGenerator>, pause: () => void, unpause: () => void } { +export function pauseableTransform (): { transform: Transform, AsyncGenerator>, pause(): void, unpause(): void } { let resolvePausePromise: ((value: unknown) => void) | undefined let pausePromise: Promise | undefined const unpause = (): void => { @@ -42,16 +42,16 @@ export function pauseableTransform (): { transform: Transform, Asy export function testClientServer (conf: StreamMuxerInit = {}): { client: StreamMuxer & { - pauseRead: () => void - unpauseRead: () => void - pauseWrite: () => void - unpauseWrite: () => void + pauseRead(): void + unpauseRead(): void + pauseWrite(): void + unpauseWrite(): void } server: StreamMuxer & { - pauseRead: () => void - unpauseRead: () => void - pauseWrite: () => void - unpauseWrite: () => void + pauseRead(): void + unpauseRead(): void + pauseWrite(): void + unpauseWrite(): void } } { const pair = duplexPair() diff --git a/test/util.ts b/test/util.ts index 3c28a7f..62aa744 100644 --- a/test/util.ts +++ b/test/util.ts @@ -44,7 +44,7 @@ export function testYamuxMuxer (name: string, client: boolean, conf: YamuxMuxerI /** * Create a transform that can be paused and unpaused */ -export function pauseableTransform (): { transform: Transform, AsyncGenerator>, pause: () => void, unpause: () => void } { +export function pauseableTransform (): { transform: Transform, AsyncGenerator>, pause(): void, unpause(): void } { let resolvePausePromise: ((value: unknown) => void) | undefined let pausePromise: Promise | undefined const unpause = (): void => { @@ -69,10 +69,10 @@ export function pauseableTransform (): { transform: Transform, Asy } export interface YamuxFixture extends YamuxMuxer { - pauseRead: () => void - unpauseRead: () => void - pauseWrite: () => void - unpauseWrite: () => void + pauseRead(): void + unpauseRead(): void + pauseWrite(): void + unpauseWrite(): void } export function testClientServer (conf: YamuxMuxerInit = {}): { diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..7627150 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": [ + "./src/index.ts", + "./src/config.ts", + "./src/stream.ts" + ] +}