@automerge/automerge-repo-network-messagechannel 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +5 -0
- package/dist/MessagePortRef.d.ts +11 -0
- package/dist/MessagePortRef.d.ts.map +1 -0
- package/dist/MessagePortRef.js +1 -0
- package/dist/StrongMessagePortRef.d.ts +10 -0
- package/dist/StrongMessagePortRef.d.ts.map +1 -0
- package/dist/StrongMessagePortRef.js +20 -0
- package/dist/WeakMessagePortRef.d.ts +12 -0
- package/dist/WeakMessagePortRef.d.ts.map +1 -0
- package/dist/WeakMessagePortRef.js +54 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/package.json +31 -0
- package/readme.md +4 -0
- package/src/MessagePortRef.ts +12 -0
- package/src/StrongMessagePortRef.ts +27 -0
- package/src/WeakMessagePortRef.ts +70 -0
- package/src/index.ts +123 -0
- package/test/index.test.ts +53 -0
- package/tsconfig.json +16 -0
package/.mocharc.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3";
|
|
2
|
+
export interface PortRefEvents {
|
|
3
|
+
message: (event: MessageEvent) => void;
|
|
4
|
+
close: () => void;
|
|
5
|
+
}
|
|
6
|
+
export interface MessagePortRef extends EventEmitter<PortRefEvents> {
|
|
7
|
+
start(): void;
|
|
8
|
+
postMessage(message: any, transferable?: Transferable[]): void;
|
|
9
|
+
isAlive(): boolean;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=MessagePortRef.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessagePortRef.d.ts","sourceRoot":"","sources":["../src/MessagePortRef.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AAExC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IACtC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,YAAY,CAAC,aAAa,CAAC;IACjE,KAAK,IAAI,IAAI,CAAA;IACb,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,YAAY,EAAE,GAAG,IAAI,CAAA;IAC9D,OAAO,IAAI,OAAO,CAAA;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3";
|
|
2
|
+
import { PortRefEvents, MessagePortRef } from "./MessagePortRef.js";
|
|
3
|
+
export declare class StrongMessagePortRef extends EventEmitter<PortRefEvents> implements MessagePortRef {
|
|
4
|
+
private port;
|
|
5
|
+
constructor(port: MessagePort);
|
|
6
|
+
postMessage(message: any, transfer: Transferable[]): void;
|
|
7
|
+
start(): void;
|
|
8
|
+
isAlive(): boolean;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=StrongMessagePortRef.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StrongMessagePortRef.d.ts","sourceRoot":"","sources":["../src/StrongMessagePortRef.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEnE,qBAAa,oBACX,SAAQ,YAAY,CAAC,aAAa,CAClC,YAAW,cAAc;IAEb,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,WAAW;IAQrC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIzD,KAAK,IAAI,IAAI;IAIb,OAAO,IAAI,OAAO;CAGnB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3";
|
|
2
|
+
export class StrongMessagePortRef extends EventEmitter {
|
|
3
|
+
port;
|
|
4
|
+
constructor(port) {
|
|
5
|
+
port.addEventListener("message", event => {
|
|
6
|
+
this.emit("message", event);
|
|
7
|
+
});
|
|
8
|
+
super();
|
|
9
|
+
this.port = port;
|
|
10
|
+
}
|
|
11
|
+
postMessage(message, transfer) {
|
|
12
|
+
this.port.postMessage(message, transfer);
|
|
13
|
+
}
|
|
14
|
+
start() {
|
|
15
|
+
this.port.start();
|
|
16
|
+
}
|
|
17
|
+
isAlive() {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3";
|
|
2
|
+
import { PortRefEvents, MessagePortRef } from "./MessagePortRef.js";
|
|
3
|
+
export declare class WeakMessagePortRef extends EventEmitter<PortRefEvents> implements MessagePortRef {
|
|
4
|
+
private weakRef;
|
|
5
|
+
private isDisconnected;
|
|
6
|
+
constructor(port: MessagePort);
|
|
7
|
+
postMessage(message: any, transfer: Transferable[]): void;
|
|
8
|
+
start(): void;
|
|
9
|
+
private disconnnect;
|
|
10
|
+
isAlive(): boolean;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=WeakMessagePortRef.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WeakMessagePortRef.d.ts","sourceRoot":"","sources":["../src/WeakMessagePortRef.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEnE,qBAAa,kBACX,SAAQ,YAAY,CAAC,aAAa,CAClC,YAAW,cAAc;IAEzB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,cAAc,CAAQ;gBAElB,IAAI,EAAE,WAAW;IAU7B,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAezD,KAAK,IAAI,IAAI;IAeb,OAAO,CAAC,WAAW;IAOnB,OAAO,IAAI,OAAO;CAYnB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3";
|
|
2
|
+
export class WeakMessagePortRef extends EventEmitter {
|
|
3
|
+
weakRef;
|
|
4
|
+
isDisconnected = false;
|
|
5
|
+
constructor(port) {
|
|
6
|
+
super();
|
|
7
|
+
this.weakRef = new WeakRef(port);
|
|
8
|
+
port.addEventListener("message", event => {
|
|
9
|
+
this.emit("message", event);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
postMessage(message, transfer) {
|
|
13
|
+
const port = this.weakRef.deref();
|
|
14
|
+
if (!port) {
|
|
15
|
+
this.disconnnect();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
port.postMessage(message, transfer);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
this.disconnnect();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
start() {
|
|
26
|
+
const port = this.weakRef.deref();
|
|
27
|
+
if (!port) {
|
|
28
|
+
this.disconnnect();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
port.start();
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
this.disconnnect();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
disconnnect() {
|
|
39
|
+
if (!this.isDisconnected) {
|
|
40
|
+
this.emit("close");
|
|
41
|
+
this.isDisconnected = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
isAlive() {
|
|
45
|
+
if (this.isDisconnected) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (!this.weakRef.deref()) {
|
|
49
|
+
this.disconnnect();
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ChannelId, NetworkAdapter, PeerId } from "@automerge/automerge-repo";
|
|
2
|
+
import { MessagePortRef } from "./MessagePortRef.js";
|
|
3
|
+
export declare class MessageChannelNetworkAdapter extends NetworkAdapter {
|
|
4
|
+
channels: {};
|
|
5
|
+
messagePortRef: MessagePortRef;
|
|
6
|
+
constructor(messagePort: MessagePort, config?: MessageChannelNetworkAdapterConfig);
|
|
7
|
+
connect(peerId: PeerId): void;
|
|
8
|
+
sendMessage(peerId: PeerId, channelId: ChannelId, uint8message: Uint8Array, broadcast: boolean): void;
|
|
9
|
+
announceConnection(channelId: ChannelId, peerId: PeerId): void;
|
|
10
|
+
join(channelId: string): void;
|
|
11
|
+
leave(docId: string): void;
|
|
12
|
+
}
|
|
13
|
+
interface MessageChannelNetworkAdapterConfig {
|
|
14
|
+
/**
|
|
15
|
+
* This is an optional parameter to use a weak ref to reference the message port that is passed to
|
|
16
|
+
* the adapter. This option is useful when using a message channel with a shared worker. If you
|
|
17
|
+
* use a network adapter with `useWeakRef = true` in the shared worker and in the main thread
|
|
18
|
+
* network adapters with strong refs the network adapter will be automatically garbage collected
|
|
19
|
+
* if you close a page. The garbage collection doesn't happen immediately; there might be some
|
|
20
|
+
* time in between when the page is closed and when the port is garbage collected
|
|
21
|
+
*/
|
|
22
|
+
useWeakRef?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAOpD,qBAAa,4BAA6B,SAAQ,cAAc;IAC9D,QAAQ,KAAK;IACb,cAAc,EAAE,cAAc,CAAA;gBAG5B,WAAW,EAAE,WAAW,EACxB,MAAM,GAAE,kCAAuC;IAWjD,OAAO,CAAC,MAAM,EAAE,MAAM;IA4CtB,WAAW,CACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,UAAU,EACxB,SAAS,EAAE,OAAO;IAmBpB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM;IAIvD,IAAI,CAAC,SAAS,EAAE,MAAM;IAQtB,KAAK,CAAC,KAAK,EAAE,MAAM;CAMpB;AAED,UAAU,kCAAkC;IAC1C;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NetworkAdapter } from "@automerge/automerge-repo";
|
|
2
|
+
import { StrongMessagePortRef } from "./StrongMessagePortRef.js";
|
|
3
|
+
import { WeakMessagePortRef } from "./WeakMessagePortRef.js";
|
|
4
|
+
import debug from "debug";
|
|
5
|
+
const log = debug("automerge-repo:messagechannel");
|
|
6
|
+
export class MessageChannelNetworkAdapter extends NetworkAdapter {
|
|
7
|
+
channels = {};
|
|
8
|
+
messagePortRef;
|
|
9
|
+
constructor(messagePort, config = {}) {
|
|
10
|
+
super();
|
|
11
|
+
const useWeakRef = config.useWeakRef ?? false;
|
|
12
|
+
this.messagePortRef = useWeakRef
|
|
13
|
+
? new WeakMessagePortRef(messagePort)
|
|
14
|
+
: new StrongMessagePortRef(messagePort);
|
|
15
|
+
}
|
|
16
|
+
connect(peerId) {
|
|
17
|
+
log("messageport connecting");
|
|
18
|
+
this.peerId = peerId;
|
|
19
|
+
this.messagePortRef.start();
|
|
20
|
+
this.messagePortRef.addListener("message", e => {
|
|
21
|
+
log("message port received", e.data);
|
|
22
|
+
const { origin, destination, type, channelId, message, broadcast } = e.data;
|
|
23
|
+
if (destination && !(destination === this.peerId || broadcast)) {
|
|
24
|
+
throw new Error("MessagePortNetwork should never receive messages for a different peer.");
|
|
25
|
+
}
|
|
26
|
+
switch (type) {
|
|
27
|
+
case "arrive":
|
|
28
|
+
this.messagePortRef.postMessage({
|
|
29
|
+
origin: this.peerId,
|
|
30
|
+
destination: origin,
|
|
31
|
+
type: "welcome",
|
|
32
|
+
});
|
|
33
|
+
this.announceConnection(channelId, origin);
|
|
34
|
+
break;
|
|
35
|
+
case "welcome":
|
|
36
|
+
this.announceConnection(channelId, origin);
|
|
37
|
+
break;
|
|
38
|
+
case "message":
|
|
39
|
+
this.emit("message", {
|
|
40
|
+
senderId: origin,
|
|
41
|
+
targetId: destination,
|
|
42
|
+
channelId,
|
|
43
|
+
message: new Uint8Array(message),
|
|
44
|
+
broadcast,
|
|
45
|
+
});
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
throw new Error("unhandled message from network");
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.messagePortRef.addListener("close", () => {
|
|
52
|
+
this.emit("close");
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
sendMessage(peerId, channelId, uint8message, broadcast) {
|
|
56
|
+
const message = uint8message.buffer.slice(uint8message.byteOffset, uint8message.byteOffset + uint8message.byteLength);
|
|
57
|
+
this.messagePortRef.postMessage({
|
|
58
|
+
origin: this.peerId,
|
|
59
|
+
destination: peerId,
|
|
60
|
+
channelId: channelId,
|
|
61
|
+
type: "message",
|
|
62
|
+
message,
|
|
63
|
+
broadcast,
|
|
64
|
+
}, [message]);
|
|
65
|
+
}
|
|
66
|
+
announceConnection(channelId, peerId) {
|
|
67
|
+
this.emit("peer-candidate", { peerId, channelId });
|
|
68
|
+
}
|
|
69
|
+
join(channelId) {
|
|
70
|
+
this.messagePortRef.postMessage({
|
|
71
|
+
origin: this.peerId,
|
|
72
|
+
channelId,
|
|
73
|
+
type: "arrive",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
leave(docId) {
|
|
77
|
+
// TODO
|
|
78
|
+
throw new Error("Unimplemented: leave on MessagePortNetworkAdapter: " + docId);
|
|
79
|
+
}
|
|
80
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@automerge/automerge-repo-network-messagechannel",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MessageChannel network adapter for Automerge Repo",
|
|
5
|
+
"repository": "https://github.com/pvh/automerge-repo",
|
|
6
|
+
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"private": false,
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"watch": "npm-watch",
|
|
14
|
+
"test": "mocha --no-warnings --experimental-specifier-resolution=node --exit"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@automerge/automerge-repo": "^0.0.1"
|
|
18
|
+
},
|
|
19
|
+
"watch": {
|
|
20
|
+
"build": {
|
|
21
|
+
"patterns": "./src/**/*",
|
|
22
|
+
"extensions": [
|
|
23
|
+
".ts"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"gitHead": "e572f26ae416140b025c3ba557d9f781abbdada1"
|
|
31
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3"
|
|
2
|
+
|
|
3
|
+
export interface PortRefEvents {
|
|
4
|
+
message: (event: MessageEvent) => void
|
|
5
|
+
close: () => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface MessagePortRef extends EventEmitter<PortRefEvents> {
|
|
9
|
+
start(): void
|
|
10
|
+
postMessage(message: any, transferable?: Transferable[]): void
|
|
11
|
+
isAlive(): boolean
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3"
|
|
2
|
+
import { PortRefEvents, MessagePortRef } from "./MessagePortRef.js"
|
|
3
|
+
|
|
4
|
+
export class StrongMessagePortRef
|
|
5
|
+
extends EventEmitter<PortRefEvents>
|
|
6
|
+
implements MessagePortRef
|
|
7
|
+
{
|
|
8
|
+
constructor(private port: MessagePort) {
|
|
9
|
+
port.addEventListener("message", event => {
|
|
10
|
+
this.emit("message", event)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
super()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
postMessage(message: any, transfer: Transferable[]): void {
|
|
17
|
+
this.port.postMessage(message, transfer)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
start(): void {
|
|
21
|
+
this.port.start()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
isAlive(): boolean {
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3"
|
|
2
|
+
import { PortRefEvents, MessagePortRef } from "./MessagePortRef.js"
|
|
3
|
+
|
|
4
|
+
export class WeakMessagePortRef
|
|
5
|
+
extends EventEmitter<PortRefEvents>
|
|
6
|
+
implements MessagePortRef
|
|
7
|
+
{
|
|
8
|
+
private weakRef: WeakRef<MessagePort>
|
|
9
|
+
private isDisconnected = false
|
|
10
|
+
|
|
11
|
+
constructor(port: MessagePort) {
|
|
12
|
+
super()
|
|
13
|
+
|
|
14
|
+
this.weakRef = new WeakRef<MessagePort>(port)
|
|
15
|
+
|
|
16
|
+
port.addEventListener("message", event => {
|
|
17
|
+
this.emit("message", event)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
postMessage(message: any, transfer: Transferable[]): void {
|
|
22
|
+
const port = this.weakRef.deref()
|
|
23
|
+
|
|
24
|
+
if (!port) {
|
|
25
|
+
this.disconnnect()
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
port.postMessage(message, transfer)
|
|
31
|
+
} catch (err) {
|
|
32
|
+
this.disconnnect()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
start(): void {
|
|
37
|
+
const port = this.weakRef.deref()
|
|
38
|
+
|
|
39
|
+
if (!port) {
|
|
40
|
+
this.disconnnect()
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
port.start()
|
|
46
|
+
} catch (err) {
|
|
47
|
+
this.disconnnect()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private disconnnect() {
|
|
52
|
+
if (!this.isDisconnected) {
|
|
53
|
+
this.emit("close")
|
|
54
|
+
this.isDisconnected = true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
isAlive(): boolean {
|
|
59
|
+
if (this.isDisconnected) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!this.weakRef.deref()) {
|
|
64
|
+
this.disconnnect()
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ChannelId, NetworkAdapter, PeerId } from "@automerge/automerge-repo"
|
|
2
|
+
import { MessagePortRef } from "./MessagePortRef.js"
|
|
3
|
+
import { StrongMessagePortRef } from "./StrongMessagePortRef.js"
|
|
4
|
+
import { WeakMessagePortRef } from "./WeakMessagePortRef.js"
|
|
5
|
+
|
|
6
|
+
import debug from "debug"
|
|
7
|
+
const log = debug("automerge-repo:messagechannel")
|
|
8
|
+
|
|
9
|
+
export class MessageChannelNetworkAdapter extends NetworkAdapter {
|
|
10
|
+
channels = {}
|
|
11
|
+
messagePortRef: MessagePortRef
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
messagePort: MessagePort,
|
|
15
|
+
config: MessageChannelNetworkAdapterConfig = {}
|
|
16
|
+
) {
|
|
17
|
+
super()
|
|
18
|
+
|
|
19
|
+
const useWeakRef = config.useWeakRef ?? false
|
|
20
|
+
|
|
21
|
+
this.messagePortRef = useWeakRef
|
|
22
|
+
? new WeakMessagePortRef(messagePort)
|
|
23
|
+
: new StrongMessagePortRef(messagePort)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
connect(peerId: PeerId) {
|
|
27
|
+
log("messageport connecting")
|
|
28
|
+
this.peerId = peerId
|
|
29
|
+
this.messagePortRef.start()
|
|
30
|
+
this.messagePortRef.addListener("message", e => {
|
|
31
|
+
log("message port received", e.data)
|
|
32
|
+
const { origin, destination, type, channelId, message, broadcast } =
|
|
33
|
+
e.data
|
|
34
|
+
if (destination && !(destination === this.peerId || broadcast)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"MessagePortNetwork should never receive messages for a different peer."
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
switch (type) {
|
|
40
|
+
case "arrive":
|
|
41
|
+
this.messagePortRef.postMessage({
|
|
42
|
+
origin: this.peerId,
|
|
43
|
+
destination: origin,
|
|
44
|
+
type: "welcome",
|
|
45
|
+
})
|
|
46
|
+
this.announceConnection(channelId, origin)
|
|
47
|
+
break
|
|
48
|
+
case "welcome":
|
|
49
|
+
this.announceConnection(channelId, origin)
|
|
50
|
+
break
|
|
51
|
+
case "message":
|
|
52
|
+
this.emit("message", {
|
|
53
|
+
senderId: origin,
|
|
54
|
+
targetId: destination,
|
|
55
|
+
channelId,
|
|
56
|
+
message: new Uint8Array(message),
|
|
57
|
+
broadcast,
|
|
58
|
+
})
|
|
59
|
+
break
|
|
60
|
+
default:
|
|
61
|
+
throw new Error("unhandled message from network")
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
this.messagePortRef.addListener("close", () => {
|
|
66
|
+
this.emit("close")
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
sendMessage(
|
|
71
|
+
peerId: PeerId,
|
|
72
|
+
channelId: ChannelId,
|
|
73
|
+
uint8message: Uint8Array,
|
|
74
|
+
broadcast: boolean
|
|
75
|
+
) {
|
|
76
|
+
const message = uint8message.buffer.slice(
|
|
77
|
+
uint8message.byteOffset,
|
|
78
|
+
uint8message.byteOffset + uint8message.byteLength
|
|
79
|
+
)
|
|
80
|
+
this.messagePortRef.postMessage(
|
|
81
|
+
{
|
|
82
|
+
origin: this.peerId,
|
|
83
|
+
destination: peerId,
|
|
84
|
+
channelId: channelId,
|
|
85
|
+
type: "message",
|
|
86
|
+
message,
|
|
87
|
+
broadcast,
|
|
88
|
+
},
|
|
89
|
+
[message]
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
announceConnection(channelId: ChannelId, peerId: PeerId) {
|
|
94
|
+
this.emit("peer-candidate", { peerId, channelId })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
join(channelId: string) {
|
|
98
|
+
this.messagePortRef.postMessage({
|
|
99
|
+
origin: this.peerId,
|
|
100
|
+
channelId,
|
|
101
|
+
type: "arrive",
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
leave(docId: string) {
|
|
106
|
+
// TODO
|
|
107
|
+
throw new Error(
|
|
108
|
+
"Unimplemented: leave on MessagePortNetworkAdapter: " + docId
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface MessageChannelNetworkAdapterConfig {
|
|
114
|
+
/**
|
|
115
|
+
* This is an optional parameter to use a weak ref to reference the message port that is passed to
|
|
116
|
+
* the adapter. This option is useful when using a message channel with a shared worker. If you
|
|
117
|
+
* use a network adapter with `useWeakRef = true` in the shared worker and in the main thread
|
|
118
|
+
* network adapters with strong refs the network adapter will be automatically garbage collected
|
|
119
|
+
* if you close a page. The garbage collection doesn't happen immediately; there might be some
|
|
120
|
+
* time in between when the page is closed and when the port is garbage collected
|
|
121
|
+
*/
|
|
122
|
+
useWeakRef?: boolean
|
|
123
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { runAdapterTests } from "@automerge/automerge-repo"
|
|
2
|
+
import { MessageChannelNetworkAdapter as Adapter } from "../src"
|
|
3
|
+
|
|
4
|
+
// bob is the hub, alice and charlie are spokes
|
|
5
|
+
describe("MessageChannelNetworkAdapter", () => {
|
|
6
|
+
runAdapterTests(async () => {
|
|
7
|
+
const aliceBobChannel = new MessageChannel()
|
|
8
|
+
const bobCharlieChannel = new MessageChannel()
|
|
9
|
+
|
|
10
|
+
const { port1: aliceToBob, port2: bobToAlice } = aliceBobChannel
|
|
11
|
+
const { port1: bobToCharlie, port2: charlieToBob } = bobCharlieChannel
|
|
12
|
+
|
|
13
|
+
const a = new Adapter(aliceToBob)
|
|
14
|
+
const b = [new Adapter(bobToAlice), new Adapter(bobToCharlie)]
|
|
15
|
+
const c = new Adapter(charlieToBob)
|
|
16
|
+
|
|
17
|
+
const teardown = () => {
|
|
18
|
+
const ports = [aliceToBob, bobToAlice, bobToCharlie, charlieToBob]
|
|
19
|
+
ports.forEach(port => port.close())
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { adapters: [a, b, c], teardown }
|
|
23
|
+
}, "hub and spoke")
|
|
24
|
+
|
|
25
|
+
// all 3 peers connected directly to each other
|
|
26
|
+
runAdapterTests(async () => {
|
|
27
|
+
const aliceBobChannel = new MessageChannel()
|
|
28
|
+
const bobCharlieChannel = new MessageChannel()
|
|
29
|
+
const aliceCharlieChannel = new MessageChannel()
|
|
30
|
+
|
|
31
|
+
const { port1: aliceToBob, port2: bobToAlice } = aliceBobChannel
|
|
32
|
+
const { port1: bobToCharlie, port2: charlieToBob } = bobCharlieChannel
|
|
33
|
+
const { port1: aliceToCharlie, port2: charlieToAlice } = aliceCharlieChannel
|
|
34
|
+
|
|
35
|
+
const a = [new Adapter(aliceToBob), new Adapter(aliceToCharlie)]
|
|
36
|
+
const b = [new Adapter(bobToAlice), new Adapter(bobToCharlie)]
|
|
37
|
+
const c = [new Adapter(charlieToBob), new Adapter(charlieToAlice)]
|
|
38
|
+
|
|
39
|
+
const teardown = () => {
|
|
40
|
+
const ports = [
|
|
41
|
+
aliceToBob,
|
|
42
|
+
bobToAlice,
|
|
43
|
+
bobToCharlie,
|
|
44
|
+
charlieToBob,
|
|
45
|
+
aliceToCharlie,
|
|
46
|
+
charlieToAlice,
|
|
47
|
+
]
|
|
48
|
+
ports.forEach(port => port.close())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { adapters: [a, b, c], teardown }
|
|
52
|
+
}, "all-to-all")
|
|
53
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"jsx": "react",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"strict": false,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*.ts"]
|
|
16
|
+
}
|