Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

docs: publish api docs #35

Merged
merged 1 commit into from
Dec 16, 2022
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: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
node_modules
coverage
.nyc_output
build
dist
.docs
.coverage
node_modules
package-lock.json
yarn.lock
docs
dist
168 changes: 16 additions & 152 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
# @libp2p/multistream-select <!-- omit in toc -->

[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![IRC](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-multistream-select.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-multistream-select)
[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/libp2p/js-libp2p-multistream-select/actions/workflows/js-test-and-release.yml)
[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-multistream-select/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p-multistream-select/actions/workflows/js-test-and-release.yml?query=branch%3Amaster)

> JavaScript implementation of multistream-select

## Table of contents <!-- omit in toc -->

- [Install](#install)
- [Browser `<script>` tag](#browser-script-tag)
- [Background](#background)
- [What is `multistream-select`?](#what-is-multistream-select)
- [Select a protocol flow](#select-a-protocol-flow)
- [Usage](#usage)
- [Dialer](#dialer)
- [Listener](#listener)
- [API](#api)
- [`mss.select(dulpex, protocols, [options])`](#mssselectdulpex-protocols-options)
- [Parameters](#parameters)
- [Returns](#returns)
- [Examples](#examples)
- [`mss.handle(duplex, protocols, [options])`](#msshandleduplex-protocols-options)
- [Parameters](#parameters-1)
- [Returns](#returns-1)
- [Examples](#examples-1)
- [Select a protocol flow](#select-a-protocol-flow)
- [API Docs](#api-docs)
- [License](#license)
- [Contribution](#contribution)

Expand All @@ -35,17 +24,25 @@
$ npm i @libp2p/multistream-select
```

### Browser `<script>` tag

Loading this module through a script tag will make it's exports available as `Libp2pMultistreamSelect` in the global namespace.

```html
<script src="https://unpkg.com/@libp2p/multistream-select/dist/index.min.js"></script>
```

## Background

### What is `multistream-select`?

TLDR; multistream-select is protocol multiplexing per connection/stream. [Full spec here](https://github.com/multiformats/multistream-select)

#### Select a protocol flow
### Select a protocol flow

The caller will send "interactive" messages, expecting for some acknowledgement from the callee, which will "select" the handler for the desired and supported protocol:

```console
```
< /multistream-select/0.3.0 # i speak multistream-select/0.3.0
> /multistream-select/0.3.0 # ok, let's speak multistream-select/0.3.0
> /ipfs-dht/0.2.3 # i want to speak ipfs-dht/0.2.3
Expand All @@ -57,142 +54,9 @@ The caller will send "interactive" messages, expecting for some acknowledgement
> <dht-message>
```

## Usage

```js
import { select, handle } from '@libp2p/multistream-select'
// You can now use
// select - actively select a protocol with a remote
// handle - handle a protocol with a remote
```

### Dialer

```js
import { pipe } from 'it-pipe'
import * as mss from '@libp2p/multistream-select'
import { Mplex } from '@libp2p/mplex'

const muxer = new Mplex()
const muxedStream = muxer.newStream()

// mss.select(protocol(s))
// Select from one of the passed protocols (in priority order)
// Returns selected stream and protocol
const { stream: dhtStream, protocol } = await mss.select(muxedStream, [
// This might just be different versions of DHT, but could be different impls
'/ipfs-dht/2.0.0', // Most of the time this will probably just be one item.
'/ipfs-dht/1.0.0'
])

// Typically this stream will be passed back to the caller of libp2p.dialProtocol
//
// ...it might then do something like this:
// try {
// await pipe(
// [uint8ArrayFromString('Some DHT data')]
// dhtStream,
// async source => {
// for await (const chunk of source)
// // DHT response data
// }
// )
// } catch (err) {
// // Error in stream
// }
```

### Listener

```js
import { pipe } from 'it-pipe'
import * as mss from '@libp2p/multistream-select'
import { Mplex } from '@libp2p/mplex'

const muxer = new Mplex({
async onStream (muxedStream) {
// mss.handle(handledProtocols)
// Returns selected stream and protocol
const { stream, protocol } = await mss.handle(muxedStream, [
'/ipfs-dht/1.0.0',
'/ipfs-bitswap/1.0.0'
])

// Typically here we'd call the handler function that was registered in
// libp2p for the given protocol:
// e.g. handlers[protocol].handler(stream)
//
// If protocol was /ipfs-dht/1.0.0 it might do something like this:
// try {
// await pipe(
// dhtStream,
// source => (async function * () {
// for await (const chunk of source)
// // Incoming DHT data -> process and yield to respond
// })(),
// dhtStream
// )
// } catch (err) {
// // Error in stream
// }
}
})
```

## API

### `mss.select(dulpex, protocols, [options])`

Negotiate a protocol to use from a list of protocols.

#### Parameters

- `duplex` (`Duplex`) - A [duplex iterable stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) to dial on.
- `protocols` (`string[]`/`string`) - A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made.
- `options` (`{ signal: AbortSignal, writeBytes?: boolean }`) - an options object containing an AbortSignal and an optional boolean `writeBytes` - if this is true, `Uint8Array`s will be written into `duplex`, otherwise `Uint8ArrayList`s will

#### Returns

`Promise<{ stream<Duplex>, protocol<string> }>` - A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`.

Note that after a protocol is selected `dialer` can no longer be used.

#### Examples

```js
const { stream, protocol } = await dialer.select([
// This might just be different versions of DHT, but could be different impls
'/ipfs-dht/2.0.0', // Most of the time this will probably just be one item.
'/ipfs-dht/1.0.0'
])
// Now talk `protocol` on `stream`
```

### `mss.handle(duplex, protocols, [options])`
## API Docs

Handle multistream protocol selections for the given list of protocols.

#### Parameters

- `duplex` (`Duplex`) - A [duplex iterable stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) to listen on.
- `protocols` (`String[]`/`String`) - A list of protocols (or single protocol) that this listener is able to speak.
- `options` (`{ signal: AbortSignal, writeBytes?: boolean }`) - an options object containing an AbortSignal and an optional boolean `writeBytes` - if this is true, `Uint8Array`s will be written into `duplex`, otherwise `Uint8ArrayList`s will

#### Returns

`Promise<{ stream<Duplex>, protocol<string> }>` - A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`.

Note that after a protocol is handled `listener` can no longer be used.

#### Examples

```js
const { stream, protocol } = await mss.handle(duplex, [
'/ipfs-dht/1.0.0',
'/ipfs-bitswap/1.0.0'
])
// Remote wants to speak `protocol`
```
- <https://libp2p.github.io/js-libp2p-multistream-select>

## License

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"types": "./dist/src/index.d.ts",
"files": [
"src",
"dist/src",
"dist",
"!dist/test",
"!**/*.tsbuildinfo"
],
Expand Down Expand Up @@ -139,7 +139,8 @@
"test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
"test:node": "aegir test -t node --cov",
"test:electron-main": "aegir test -t electron-main",
"release": "aegir release"
"release": "aegir release",
"docs": "aegir docs"
},
"dependencies": {
"@libp2p/interfaces": "^3.0.2",
Expand Down
46 changes: 46 additions & 0 deletions src/handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,52 @@ import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream

const log = logger('libp2p:mss:handle')

/**
* Handle multistream protocol selections for the given list of protocols.
*
* Note that after a protocol is handled `listener` can no longer be used.
*
* @param stream - A duplex iterable stream to listen on
* @param protocols - A list of protocols (or single protocol) that this listener is able to speak.
* @param options - an options object containing an AbortSignal and an optional boolean `writeBytes` - if this is true, `Uint8Array`s will be written into `duplex`, otherwise `Uint8ArrayList`s will
* @returns A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`
* @example
*
* ```js
* import { pipe } from 'it-pipe'
* import * as mss from '@libp2p/multistream-select'
* import { Mplex } from '@libp2p/mplex'
*
* const muxer = new Mplex({
* async onStream (muxedStream) {
* // mss.handle(handledProtocols)
* // Returns selected stream and protocol
* const { stream, protocol } = await mss.handle(muxedStream, [
* '/ipfs-dht/1.0.0',
* '/ipfs-bitswap/1.0.0'
* ])
*
* // Typically here we'd call the handler function that was registered in
* // libp2p for the given protocol:
* // e.g. handlers[protocol].handler(stream)
* //
* // If protocol was /ipfs-dht/1.0.0 it might do something like this:
* // try {
* // await pipe(
* // dhtStream,
* // source => (async function * () {
* // for await (const chunk of source)
* // // Incoming DHT data -> process and yield to respond
* // })(),
* // dhtStream
* // )
* // } catch (err) {
* // // Error in stream
* // }
* }
* })
* ```
*/
export async function handle (stream: Duplex<Uint8Array>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>
export async function handle (stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>
export async function handle (stream: Duplex<any>, protocols: string | string[], options?: MultistreamSelectInit): Promise<ProtocolStream<any>> {
Expand Down
43 changes: 43 additions & 0 deletions src/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,49 @@ import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream

const log = logger('libp2p:mss:select')

/**
* Negotiate a protocol to use from a list of protocols.
*
* @param stream - A duplex iterable stream to dial on
* @param protocols - A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made.
* @param options - An options object containing an AbortSignal and an optional boolean `writeBytes` - if this is true, `Uint8Array`s will be written into `duplex`, otherwise `Uint8ArrayList`s will
* @returns A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`.
* @example
*
* ```js
* import { pipe } from 'it-pipe'
* import * as mss from '@libp2p/multistream-select'
* import { Mplex } from '@libp2p/mplex'
*
* const muxer = new Mplex()
* const muxedStream = muxer.newStream()
*
* // mss.select(protocol(s))
* // Select from one of the passed protocols (in priority order)
* // Returns selected stream and protocol
* const { stream: dhtStream, protocol } = await mss.select(muxedStream, [
* // This might just be different versions of DHT, but could be different impls
* '/ipfs-dht/2.0.0', // Most of the time this will probably just be one item.
* '/ipfs-dht/1.0.0'
* ])
*
* // Typically this stream will be passed back to the caller of libp2p.dialProtocol
* //
* // ...it might then do something like this:
* // try {
* // await pipe(
* // [uint8ArrayFromString('Some DHT data')]
* // dhtStream,
* // async source => {
* // for await (const chunk of source)
* // // DHT response data
* // }
* // )
* // } catch (err) {
* // // Error in stream
* // }
* ```
*/
export async function select (stream: Duplex<Uint8Array>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>
export async function select (stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>
export async function select (stream: Duplex<any>, protocols: string | string[], options: MultistreamSelectInit = {}): Promise<ProtocolStream<any>> {
Expand Down