Skip to content

Commit

Permalink
Convert to using [NetworkAdapterInterface] (#4)
Browse files Browse the repository at this point in the history
* rename

* Update README.md

* export t ypes

* 0.0.12

* Update README.md

* emit message events

* implement [NetworkAdapterInterface]

dropping deprecated [NetworkAdapter] inherits.

* Update README.md

* 1.1.1

* Update NetworkAdapter.test.ts
  • Loading branch information
philcockfield committed Feb 28, 2024
1 parent 2daf2e4 commit 7d46c85
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 22 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
#### Security


## [1.1.1] - 2024-02-28
#### Added
- onData callback method (replacing the rxjs observable pattern from original implementation).
- Export types from package.
- Converted adapter to implement [NetworkAdapterInterface] as [NetworkAdapter] base abstract class deprecated.
#### Changed
- Rename `WebrtcNetworkAdapter` to `PeerjsNetworkAdapter`
- Version set to `1.1.1` to match main `automerge-repo` version.



## [0.0.1] - 2024-02-27
#### Added
Expand Down
34 changes: 31 additions & 3 deletions README.md
@@ -1,15 +1,43 @@
[![ci.node](https://github.com/philcockfield/automerge-repo-network-peerjs/actions/workflows/ci.node.yml/badge.svg)](https://github.com/philcockfield/automerge-repo-network-peerjs/actions/workflows/ci.node.yml)
# automerge-repo-network-peerjs
[![ci.node](https://github.com/philcockfield/automerge-repo-network-peerjs/actions/workflows/ci.node.yml/badge.svg)](https://github.com/philcockfield/automerge-repo-network-peerjs/actions/workflows/ci.node.yml)

A network adapter for WebRTC, based on the point-to-point [MessageChannelNetworkAdapter](https://github.com/automerge/automerge-repo/blob/main/packages/automerge-repo-network-messagechannel/src/index.ts).


**Please Note:** This is not an official part of the [automerge-repo](https://github.com/automerge/automerge-repo) project, rather a community contribution that includes a dependency on the [peerjs](https://github.com/peers/peerjs) library.

## Setup

```
yarn add automerge-repo-network-peerjs
```

## Licence: MIT
## Usage

Establish a data connection as per [peerjs](https://github.com/peers/peerjs#data-connections) documentation:

```ts
import { Peer } from "peerjs";
const conn = peer.connect("another-peers-id");
```

Then use that to pass into the constructor of the automerge network adapter:

```ts
import { PeerjsNetworkAdapter } from 'automerge-repo-network-peerjs';
const adapter = new PeerjsNetworkAdapter(conn);
```

Along with the usual `NetworkAdapterInterface` events an additional `onData` event is available to
to keep track of directional data being sent and received, for example:

```ts
function monitor(adapter: PeerjsNetworkAdapter, dispose$?: Observable<any>) {
const detach = adapter.onData((e) => console.log(`⚡️ ${e.direction}: ${e.bytes} bytes`));
dispose$?.subscribe(() => detach());
}

```


## Licence
MIT
3 changes: 2 additions & 1 deletion package.json
@@ -1,11 +1,12 @@
{
"name": "automerge-repo-network-peerjs",
"version": "0.0.1",
"version": "1.1.1",
"description": "Network adapter for automerge-repo using peerjs",
"author": "phil Cockfield",
"license": "MIT",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "vitest"
Expand Down
5 changes: 5 additions & 0 deletions src/NetworkAdapter.test.ts
@@ -1,6 +1,11 @@
import { describe, expect, it } from "vitest";

describe("NetworkAdapter", () => {
/**
* NOTE: WebRTC is a browser based technology so server-side testing is hard.
* Over time, if it becomes necessary, we can expand these tests out
* to include mocking.
*/
it("placeholder", () => {
expect(123).to.eql(123);
});
Expand Down
44 changes: 33 additions & 11 deletions src/NetworkAdapter.ts
@@ -1,6 +1,7 @@
import { NetworkAdapter } from "@automerge/automerge-repo";
import { EventEmitter } from "eventemitter3";
import type * as t from "./t.js";
import type * as t from "./types.js";

type EventTypes = { data: t.NetworkMessageAlert };

/**
* An Automerge repo network-adapter for WebRTC (P2P)
Expand All @@ -10,25 +11,32 @@ import type * as t from "./t.js";
* https://github.com/automerge/automerge-repo/blob/main/packages/automerge-repo-network-messagechannel/src/index.ts
*
*/
export class WebrtcNetworkAdapter extends NetworkAdapter {
#conn: t.DataConnection;
export class PeerjsNetworkAdapter
extends EventEmitter<t.NetworkAdapterEvents>
implements t.NetworkAdapterInterface
{
peerId?: t.PeerId;
peerMetadata?: t.PeerMetadata;

#isReady = false;
#disconnected = new EventEmitter<"disconnected">();
#conn: t.DataConnection;
#events = new EventEmitter<EventTypes>();

constructor(conn: t.DataConnection) {
if (!conn) throw new Error(`A peerjs data-connection is required`);
super();
this.#conn = conn;
}

connect(peerId: t.PeerId) {
connect(peerId: t.PeerId, meta?: t.PeerMetadata) {
const senderId = (this.peerId = peerId);
const conn = this.#conn;
const peerMetadata = meta ?? {};

const handleOpen = () => this.#transmit({ type: "arrive", senderId, peerMetadata: {} });
const handleOpen = () => this.#transmit({ type: "arrive", senderId, peerMetadata });
const handleClose = () => this.emit("close");
const handleData = (e: any) => {
const msg = e as t.WebrtcMessage;
const msg = e as t.NetworkMessage;

/**
* Arrive.
Expand Down Expand Up @@ -56,13 +64,14 @@ export class WebrtcNetworkAdapter extends NetworkAdapter {
let payload = msg as t.Message;
if ("data" in msg) payload = { ...payload, data: toUint8Array(msg.data!) };
this.emit("message", payload);
this.#alert("incoming", msg);
};

conn.on("open", handleOpen);
conn.on("close", handleClose);
conn.on("data", handleData);

this.#disconnected.on("disconnected", () => {
this.on("peer-disconnected", () => {
this.#isReady = false;
conn.off("open", handleOpen);
conn.off("close", handleClose);
Expand All @@ -78,7 +87,13 @@ export class WebrtcNetworkAdapter extends NetworkAdapter {
}

disconnect() {
this.#disconnected.emit("disconnected");
const peerId = this.peerId;
if (peerId) this.emit("peer-disconnected", { peerId });
}

onData(fn: (e: t.NetworkMessageAlert) => void) {
this.#events.on("data", fn);
return () => this.#events.off("data", fn);
}

send(message: t.RepoMessage) {
Expand All @@ -90,9 +105,16 @@ export class WebrtcNetworkAdapter extends NetworkAdapter {
}
}

#transmit(message: t.WebrtcMessage) {
#transmit(message: t.NetworkMessage) {
if (!this.#conn) throw new Error("Connection not ready");
this.#conn.send(message);
this.#alert("outgoing", message);
}

#alert(direction: t.IODirection, message: t.NetworkMessage) {
const bytes = "data" in message ? message.data?.byteLength ?? 0 : 0;
const payload: t.NetworkMessageAlert = { direction, message, bytes };
this.#events.emit("data", payload);
}

#setAsReady() {
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
@@ -1 +1,2 @@
export { WebrtcNetworkAdapter } from "./NetworkAdapter.js";
export { PeerjsNetworkAdapter } from "./NetworkAdapter.js";
export type { NetworkMessage, NetworkMessageAlert } from "./types.js";
28 changes: 22 additions & 6 deletions src/t.ts → src/types.ts
@@ -1,8 +1,22 @@
/**
* @automerge
*/
import type { Message, PeerId, RepoMessage, StorageId } from "@automerge/automerge-repo";
export { Message, PeerId, RepoMessage };
import type {
Message,
NetworkAdapterEvents,
NetworkAdapterInterface,
PeerId,
RepoMessage,
StorageId,
} from "@automerge/automerge-repo";
export type {
Message,
NetworkAdapterEvents,
NetworkAdapterInterface,
PeerId,
RepoMessage,
StorageId,
};

/**
* @peerjs
Expand All @@ -15,12 +29,14 @@ export type { DataConnection } from "peerjs";
* MessageChannelNetworkAdapter
* https://github.com/automerge/automerge-repo/blob/main/packages/automerge-repo-network-messagechannel/src/index.ts
*/
export type IODirection = "incoming" | "outgoing";
export type { PeerjsNetworkAdapter } from "./NetworkAdapter.js";

export type WebrtcMessage = ArriveMessage | WelcomeMessage | Message;
export type WebrtcMessageAlert = {
export type IODirection = "incoming" | "outgoing";
export type NetworkMessage = ArriveMessage | WelcomeMessage | Message;
export type NetworkMessageAlert = {
direction: IODirection;
message: WebrtcMessage;
message: NetworkMessage;
bytes: number;
};

/**
Expand Down

0 comments on commit 7d46c85

Please sign in to comment.