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

Commit

Permalink
docs: publish api docs (#35)
Browse files Browse the repository at this point in the history
Update project config to publish api docs
  • Loading branch information
achingbrain authored Dec 16, 2022
1 parent 2c9e6f0 commit c4c978a
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 158 deletions.
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

0 comments on commit c4c978a

Please sign in to comment.