-
Notifications
You must be signed in to change notification settings - Fork 16
Feature/refactor and add tests and coverage #60
Changes from all commits
c8c329e
6d7c528
37794cd
232d171
1c7b463
4599d33
041a057
d42e613
86dd1d2
3b78322
7d83c81
57a0635
581ca72
9d3afb0
0a6ce42
584c6fa
a1df8c3
dbf9bd4
f254822
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ public/css/main.css | |
|
||
# Coverage reports | ||
coverage | ||
.coverage | ||
.nyc_output | ||
|
||
# API keys and secrets | ||
.env | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3,22 +3,23 @@ | |||||
[](http://libp2p.io/) | ||||||
[](http://webchat.freenode.net/?channels=%23libp2p) | ||||||
[](https://discuss.libp2p.io) | ||||||
[](https://codecov.io/gh/little-bear-labs//js-libp2p-webrtc) | ||||||
[](https://github.com/little-bear-labs//js-libp2p-webrtc/actions/workflows/js-test-and-release.yml) | ||||||
[](https://codecov.io/gh/little-bear-labs/js-libp2p-webrtc) | ||||||
[](https://github.com/little-bear-labs/js-libp2p-webrtc/actions/workflows/js-test-and-release.yml) | ||||||
|
||||||
> The browser implementation of the WebRTC module for libp2p. | ||||||
|
||||||
## Table of contents <!-- omit in toc --> | ||||||
|
||||||
- [Install](#install) | ||||||
- [Usage](#usage) | ||||||
- [API](#api) | ||||||
- [Examples](#examples) | ||||||
- [Interfaces](#interfaces) | ||||||
- [Transport](#transport) | ||||||
- [Connection](#connection) | ||||||
- [Hacking](#hacking) | ||||||
- [Contribute](#contribute) | ||||||
- [Development](#development) | ||||||
- [Build](#build) | ||||||
- [Protocol Buffers](#protocol-buffers) | ||||||
- [Test](#test) | ||||||
- [Lint](#lint) | ||||||
- [Clean](#clean) | ||||||
- [Check Dependencies](#check-dependencies) | ||||||
|
@@ -35,49 +36,81 @@ npm i @libp2p/webrtc | |||||
## Usage | ||||||
|
||||||
```js | ||||||
import { createLibp2pNode } from 'libp2p' | ||||||
import { webRTC } from '@libp2p/webrtc' | ||||||
import { noise } from '@chainsafe/libp2p-noise' | ||||||
import { createLibp2p } from 'libp2p' | ||||||
import { Noise } from '@chainsafe/libp2p-noise' | ||||||
import { multiaddr } from '@multiformats/multiaddr' | ||||||
import { pipe } from 'it-pipe' | ||||||
import all from 'it-all' | ||||||
|
||||||
const node = await createLibp2pNode({ | ||||||
transports: [ | ||||||
webRTC() | ||||||
], | ||||||
connectionEncryption: [ | ||||||
noise() | ||||||
] | ||||||
}) | ||||||
|
||||||
const addr = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ') | ||||||
const { stream } = await node.dialProtocol(addr, '/my-protocol/1.0.0') | ||||||
const values = await pipe(stream, all) | ||||||
import first from "it-first"; | ||||||
import { pipe } from "it-pipe"; | ||||||
import { fromString, toString } from "uint8arrays"; | ||||||
import { webRTC } from 'js-libp2p-webrtc' | ||||||
|
||||||
const node = await createLibp2p({ | ||||||
transports: [webRTC()], | ||||||
connectionEncryption: [() => new Noise()], | ||||||
}); | ||||||
|
||||||
await node.start() | ||||||
|
||||||
const ma = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ') | ||||||
const stream = await node.dialProtocol(ma, ['/my-protocol/1.0.0']) | ||||||
const message = `Hello js-libp2p-webrtc\n` | ||||||
const response = await pipe([fromString(message)], stream, async (source) => await first(source)) | ||||||
const responseDecoded = toString(response.slice(0, response.length)) | ||||||
``` | ||||||
## API | ||||||
|
||||||
## Examples | ||||||
Examples can be found in the [examples folder](examples/README.md). | ||||||
|
||||||
## Interfaces | ||||||
|
||||||
### Transport | ||||||
|
||||||
[](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/transport) | ||||||
 | ||||||
|
||||||
`libp2p-webrtc` accepts WebRTC encapsulated addresses: `/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ` | ||||||
Browsers can only `dial`, so `listen` is not supported. | ||||||
|
||||||
### Connection | ||||||
```js | ||||||
interface Transport { | ||||||
[Symbol.toStringTag]: string | ||||||
[symbol]: true | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this line do? |
||||||
dial: (ma: Multiaddr, options: DialOptions) => Promise<Connection> | ||||||
createListener: (options: CreateListenerOptions) => Listener | ||||||
filter: MultiaddrFilter | ||||||
} | ||||||
|
||||||
class WebRTCTransport implements Transport { | ||||||
|
||||||
async dial (ma: Multiaddr, options: WebRTCDialOptions): Promise<Connection> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Given options implies it is optional, not better to make it clear that the object as a whole is optional? Or is there value in enforcing Or is it because there are properties in there required, but if so, it probably shouldn't be part of options? |
||||||
const rawConn = await this._connect(ma, options) | ||||||
log(`dialing address - ${ma.toString()}`) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Fairly certain that
https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2/ seems to agree with this as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also very minor, and perhaps I miss a convention here, but the log says
or else it perhaps is better to say |
||||||
return rawConn | ||||||
} | ||||||
|
||||||
createListener (options: CreateListenerOptions): Listener { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you turn your class into an abstract class (
without having to specify your dummy body. |
||||||
throw unimplemented('WebRTCTransport.createListener') | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
[](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/connection) | ||||||
### Connection | ||||||
|
||||||
## Hacking | ||||||
 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Not more accessible? |
||||||
|
||||||
Besides the usual `npm install` to get dependencies, `npm run build` to invoke tsc, and `npm run test` to execute unit tests... | ||||||
```js | ||||||
interface MultiaddrConnection extends Duplex<Uint8Array> { | ||||||
close: (err?: Error) => Promise<void> | ||||||
remoteAddr: Multiaddr | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it has to be writable or can it be |
||||||
timeline: MultiaddrConnectionTimeline | ||||||
} | ||||||
|
||||||
There is also `npm run autogen` which uses ProtoBuf's protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files. | ||||||
class WebRTCMultiaddrConnection implements MultiaddrConnection { } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any value to this example as it:
Just asking as people do tend to copy examples as is. |
||||||
``` | ||||||
|
||||||
## Contribute | ||||||
|
||||||
Contributions are welcome! The libp2p implementation in JavaScript is a work in progress. As such, there's a few things you can do right now to help out: | ||||||
|
||||||
- [Check out the existing issues](//github.com/little-bear-labs//js-libp2p-webrtc/issues). | ||||||
- [Check out the existing issues](//github.com/little-bear-labs/js-libp2p-webrtc/issues). | ||||||
- **Perform code reviews**. | ||||||
- **Add tests**. There can never be enough tests. | ||||||
- 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. | ||||||
|
@@ -86,8 +119,6 @@ Please be aware that all interactions related to libp2p are subject to the IPFS | |||||
|
||||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. | ||||||
|
||||||
## Development | ||||||
|
||||||
This module leans heavily on (Aegir)[https://github.com/ipfs/aegir] for most of the `package.json` scripts. | ||||||
|
||||||
### Build | ||||||
|
@@ -99,6 +130,30 @@ npm run build | |||||
|
||||||
The build will be located in the `/dist` folder. | ||||||
|
||||||
### Protocol Buffers | ||||||
|
||||||
There is also `npm run generate:proto` script that uses protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files. | ||||||
|
||||||
### Test | ||||||
|
||||||
To run all tests: | ||||||
|
||||||
```shell | ||||||
npm test | ||||||
``` | ||||||
|
||||||
To run tests for Chome only: | ||||||
|
||||||
```shell | ||||||
npm run test:chrome | ||||||
``` | ||||||
|
||||||
To run tests for Firefox only: | ||||||
|
||||||
```shell | ||||||
npm run test:firefox | ||||||
``` | ||||||
|
||||||
### Lint | ||||||
Aegir is also used to lint the code, which follows the [Standard](https://github.com/standard/standard) JS linter. | ||||||
The VS Code plugin for this standard is located at https://marketplace.visualstudio.com/items?itemName=standard.vscode-standard. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Examples | ||
|
||
* [Browser to Server Echo](browser-to-server/README.md): connect to a go-libp2p-webrtc server with a browser | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,34 @@ | ||||||
# js-libp2p-webrtc Browser to Server | ||||||
|
||||||
This example leverages the [vite bundler](https://vitejs.dev/) to compile and serve the libp2p code in the browser. You can use other bundlers such as Webpack, but we will not be covering them here. | ||||||
|
||||||
## Running the Go Server | ||||||
|
||||||
To run the Go LibP2P WebRTC server: | ||||||
|
||||||
```shell | ||||||
npm run go-libp2p-server | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I to understand correctly that this command assumes you have go installed already? And at whatever version is necessary? Is it possible npm could make sure that's true for you? |
||||||
``` | ||||||
|
||||||
Copy the multiaddress in the output. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might be useful to show an example of such output and multiaddress |
||||||
|
||||||
## Running the Example | ||||||
|
||||||
In a separate console tab, install dependencies and start the Vite server: | ||||||
|
||||||
```shell | ||||||
npm i && npm run start | ||||||
``` | ||||||
|
||||||
The browser window will automatically open. | ||||||
Using the copied multiaddress from the Go server, paste it into the `Server MultiAddress` input and click the `Connect` button. | ||||||
Once the peer is connected, click the message section will appear. Enter a message and click the `Send` button. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? |
||||||
|
||||||
The output should look like: | ||||||
|
||||||
```text | ||||||
Dialing /ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb | ||||||
Peer connected '/ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb' | ||||||
Sending message 'hello' | ||||||
Received message 'hello' | ||||||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>js-libp2p WebRTC</title> | ||
<style> | ||
label, | ||
button { | ||
display: block; | ||
font-weight: bold; | ||
margin: 5px 0; | ||
} | ||
div { | ||
margin-bottom: 20px; | ||
} | ||
#send-section { | ||
display: none; | ||
} | ||
input[type='text'] { | ||
width: 800px; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="app"> | ||
<div> | ||
<label for="peer">Server MultiAddress:</label> | ||
<input type="text" id="peer" /> | ||
<button id="connect">Connect</button> | ||
</div> | ||
<div id="send-section"> | ||
<label for="message">Message:</label> | ||
<input type="text" id="message" value="hello" /> | ||
<button id="send">Send</button> | ||
</div> | ||
<div id="output"></div> | ||
</div> | ||
<script type="module" src="./index.js"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { createLibp2p } from 'libp2p' | ||
import { Noise } from '@chainsafe/libp2p-noise' | ||
import { multiaddr } from '@multiformats/multiaddr' | ||
import first from "it-first"; | ||
import { pipe } from "it-pipe"; | ||
import { fromString, toString } from "uint8arrays"; | ||
import { webRTC } from 'js-libp2p-webrtc' | ||
|
||
let stream; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this indention on purpose? o.O There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or did you forget to define There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even better, probably only want to do it once you know the DOM is initialised/ready? |
||
const output = document.getElementById('output') | ||
const sendSection = document.getElementById('send-section') | ||
const appendOutput = (line) => output.innerText += `${line}\n` | ||
const clean = (line) => line.replaceAll('\n', '') | ||
|
||
const node = await createLibp2p({ | ||
transports: [webRTC()], | ||
connectionEncryption: [() => new Noise()], | ||
}); | ||
|
||
await node.start() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this even run? Since when can you have |
||
|
||
node.connectionManager.addEventListener('peer:connect', (connection) => { | ||
appendOutput(`Peer connected '${node.getConnections().map(c => c.remoteAddr.toString())}'`) | ||
sendSection.style.display = 'block' | ||
}) | ||
|
||
window.connect.onclick = async () => { | ||
const ma = multiaddr(window.peer.value) | ||
appendOutput(`Dialing ${ma}`) | ||
stream = await node.dialProtocol(ma, ['/echo/1.0.0']) | ||
} | ||
|
||
window.send.onclick = async () => { | ||
const message = `${window.message.value}\n` | ||
appendOutput(`Sending message '${clean(message)}'`) | ||
const response = await pipe([fromString(message)], stream, async (source) => await first(source)) | ||
const responseDecoded = toString(response.slice(0, response.length)); | ||
appendOutput(`Received message '${clean(responseDecoded)}'`) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading up I found out what the implementation of this line would do, but I do not get why it is useful to declare here?