@agent-p2p/peer 0.0.4 → 0.0.5
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/dist/radio-peer.d.ts.map +1 -1
- package/dist/radio-peer.js +58 -52
- package/dist/radio-peer.js.map +1 -1
- package/dist/transport/trystero-provider.d.ts +7 -0
- package/dist/transport/trystero-provider.d.ts.map +1 -1
- package/dist/transport/trystero-provider.js +77 -28
- package/dist/transport/trystero-provider.js.map +1 -1
- package/package.json +1 -1
- package/src/radio-peer.ts +56 -51
- package/src/transport/trystero-provider.ts +74 -27
package/dist/radio-peer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-peer.d.ts","sourceRoot":"","sources":["../src/radio-peer.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,OAAO,EAIZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK3E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AASpE,qBAAa,SAAU,SAAQ,YAAY,CAAC,YAAY,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,KAAK,CAAsE;IACnF,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,IAAI,CAAqC;IACjD,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,WAAW,CAA6B;gBAEpC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB;YAQ5C,KAAK;
|
|
1
|
+
{"version":3,"file":"radio-peer.d.ts","sourceRoot":"","sources":["../src/radio-peer.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,OAAO,EAIZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK3E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AASpE,qBAAa,SAAU,SAAQ,YAAY,CAAC,YAAY,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,KAAK,CAAsE;IACnF,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,IAAI,CAAqC;IACjD,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,WAAW,CAA6B;gBAEpC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB;YAQ5C,KAAK;IA6Eb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,WAAW,IAAI,eAAe,CAEjC;IAEK,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBpE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI;IAe1E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE;IAI3C,QAAQ,IAAI,QAAQ,EAAE;IAIhB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAYjC"}
|
package/dist/radio-peer.js
CHANGED
|
@@ -33,63 +33,69 @@ export class RadioPeer extends TypedEmitter {
|
|
|
33
33
|
}
|
|
34
34
|
async _init() {
|
|
35
35
|
this.emit("status", "connecting");
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
else {
|
|
40
|
-
this._keypair = await generateKeypair();
|
|
41
|
-
}
|
|
42
|
-
this._peerId = peerId(this._keypair.publicKey);
|
|
43
|
-
const guard = (pid, topic) => {
|
|
44
|
-
return this._permissions.canPublish(pid, topic);
|
|
45
|
-
};
|
|
46
|
-
this._docManager = new DocManager(this._opts.maxSignalsPerChannel, guard);
|
|
47
|
-
this._permissions = new RoomPermissions(this._docManager.doc);
|
|
48
|
-
const roomName = `agent-p2p:${this.stationId}`;
|
|
49
|
-
const transport = this._opts.transport;
|
|
50
|
-
this._provider = new TrysteroProvider(this._docManager.doc, roomName, {
|
|
51
|
-
backend: transport.backend,
|
|
52
|
-
password: this._opts.password || undefined,
|
|
53
|
-
relayUrls: transport.relayUrls,
|
|
54
|
-
});
|
|
55
|
-
await this._provider.connected;
|
|
56
|
-
if (this._opts.enablePersistence) {
|
|
57
|
-
try {
|
|
58
|
-
this._idb = createIndexedDBProvider(this._docManager.doc, roomName);
|
|
36
|
+
try {
|
|
37
|
+
if (this._opts.keypair) {
|
|
38
|
+
this._keypair = this._opts.keypair;
|
|
59
39
|
}
|
|
60
|
-
|
|
61
|
-
|
|
40
|
+
else {
|
|
41
|
+
this._keypair = await generateKeypair();
|
|
62
42
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!this._knownPeers.has(p.peerId)) {
|
|
81
|
-
this._knownPeers.add(p.peerId);
|
|
82
|
-
this.emit("peer:join", p);
|
|
43
|
+
this._peerId = peerId(this._keypair.publicKey);
|
|
44
|
+
const guard = (pid, topic) => {
|
|
45
|
+
return this._permissions.canPublish(pid, topic);
|
|
46
|
+
};
|
|
47
|
+
this._docManager = new DocManager(this._opts.maxSignalsPerChannel, guard);
|
|
48
|
+
this._permissions = new RoomPermissions(this._docManager.doc);
|
|
49
|
+
const roomName = `agent-p2p:${this.stationId}`;
|
|
50
|
+
const transport = this._opts.transport;
|
|
51
|
+
this._provider = new TrysteroProvider(this._docManager.doc, roomName, {
|
|
52
|
+
backend: transport.backend,
|
|
53
|
+
password: this._opts.password || undefined,
|
|
54
|
+
relayUrls: transport.relayUrls,
|
|
55
|
+
});
|
|
56
|
+
await this._provider.connected;
|
|
57
|
+
if (this._opts.enablePersistence) {
|
|
58
|
+
try {
|
|
59
|
+
this._idb = createIndexedDBProvider(this._docManager.doc, roomName);
|
|
83
60
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (!currentIds.has(id)) {
|
|
87
|
-
this._knownPeers.delete(id);
|
|
88
|
-
this.emit("peer:leave", { peerId: id, joinedAt: 0 });
|
|
61
|
+
catch {
|
|
62
|
+
// IndexedDB not available (e.g. Node.js) — skip
|
|
89
63
|
}
|
|
90
64
|
}
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
setLocalPeer(this._provider.awareness, this._peerId);
|
|
66
|
+
// TOFU: first peer becomes owner, later peers become members
|
|
67
|
+
if (!this._permissions.isInitialized) {
|
|
68
|
+
this._permissions.initializeAsOwner(this._peerId);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this._permissions.acceptExisting(this._peerId);
|
|
72
|
+
}
|
|
73
|
+
// Forward role changes as events
|
|
74
|
+
this._unsubRoles = this._permissions.observeRoles((pid, role) => {
|
|
75
|
+
this.emit("roles:changed", { peerId: pid, role });
|
|
76
|
+
});
|
|
77
|
+
this._provider.awareness.on("change", () => {
|
|
78
|
+
const current = getConnectedPeers(this._provider.awareness);
|
|
79
|
+
const currentIds = new Set(current.map((p) => p.peerId));
|
|
80
|
+
for (const p of current) {
|
|
81
|
+
if (!this._knownPeers.has(p.peerId)) {
|
|
82
|
+
this._knownPeers.add(p.peerId);
|
|
83
|
+
this.emit("peer:join", p);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const id of this._knownPeers) {
|
|
87
|
+
if (!currentIds.has(id)) {
|
|
88
|
+
this._knownPeers.delete(id);
|
|
89
|
+
this.emit("peer:leave", { peerId: id, joinedAt: 0 });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
this.emit("status", "connected");
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
this.emit("status", "disconnected");
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
async ready() {
|
|
95
101
|
return this._readyPromise;
|
package/dist/radio-peer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-peer.js","sourceRoot":"","sources":["../src/radio-peer.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,eAAe,EACf,MAAM,EACN,aAAa,GACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE,MAAM,eAAe,GAAgD;IACpE,QAAQ,EAAE,EAAE;IACZ,oBAAoB,EAAE,IAAI;IAC1B,iBAAiB,EAAE,IAAI;IACvB,SAAS,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;CACjC,CAAC;AAEF,MAAM,OAAO,SAAU,SAAQ,YAA0B;IAC/C,SAAS,CAAS;IACnB,QAAQ,CAAW;IACnB,OAAO,CAAU;IACjB,KAAK,CAAsE;IAC3E,WAAW,CAAc;IACzB,SAAS,CAAoB;IAC7B,IAAI,GAAgC,IAAI,CAAC;IACzC,cAAc,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC/C,aAAa,CAAgB;IAC7B,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,YAAY,CAAmB;IAC/B,WAAW,GAAwB,IAAI,CAAC;IAEhD,YAAY,SAAiB,EAAE,OAAyB,EAAE;QACzD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC;QACtD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IACnC,CAAC;IAEO,KAAK,CAAC,KAAK;QAClB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAElC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"radio-peer.js","sourceRoot":"","sources":["../src/radio-peer.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,8 +23,15 @@ export declare class TrysteroProvider {
|
|
|
23
23
|
private _connect;
|
|
24
24
|
private _sendSyncStep1;
|
|
25
25
|
private _sendAwareness;
|
|
26
|
+
/**
|
|
27
|
+
* Extract Yjs awareness clientIDs from a raw awareness update binary.
|
|
28
|
+
* Format: [count: varuint] [entries: {clientId: varuint, clock: varuint, state: varstring}...]
|
|
29
|
+
*/
|
|
30
|
+
private _trackPeerClients;
|
|
26
31
|
private _handleMessage;
|
|
32
|
+
/** Broadcast data to all connected peers. Fire-and-forget with error suppression. */
|
|
27
33
|
private _broadcast;
|
|
34
|
+
/** Send data to a specific peer. Fire-and-forget with error suppression. */
|
|
28
35
|
private _sendTo;
|
|
29
36
|
destroy(): Promise<void>;
|
|
30
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trystero-provider.d.ts","sourceRoot":"","sources":["../../src/transport/trystero-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,SAAS,EAAsE,MAAM,uBAAuB,CAAC;AAKtH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,MAAM,WAAW,cAAc;IAC9B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAID,qBAAa,gBAAgB;IAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAgD;IACpE,oFAAoF;IACpF,OAAO,CAAC,YAAY,CAAkC;gBAE1C,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,cAAmB;YAgBvD,QAAQ;
|
|
1
|
+
{"version":3,"file":"trystero-provider.d.ts","sourceRoot":"","sources":["../../src/transport/trystero-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,SAAS,EAAsE,MAAM,uBAAuB,CAAC;AAKtH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,MAAM,WAAW,cAAc;IAC9B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAID,qBAAa,gBAAgB;IAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAgD;IACpE,oFAAoF;IACpF,OAAO,CAAC,YAAY,CAAkC;gBAE1C,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,cAAmB;YAgBvD,QAAQ;IAsFtB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,cAAc;IAStB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,cAAc;IAkCtB,qFAAqF;IACrF,OAAO,CAAC,UAAU;IASlB,4EAA4E;IAC5E,OAAO,CAAC,OAAO;IAST,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAmC9B"}
|
|
@@ -22,7 +22,7 @@ export class TrysteroProvider {
|
|
|
22
22
|
this.roomName = roomName;
|
|
23
23
|
this.awareness = new Awareness(doc);
|
|
24
24
|
this._onDocUpdate = (update, origin) => {
|
|
25
|
-
if (origin === this)
|
|
25
|
+
if (this._destroyed || origin === this)
|
|
26
26
|
return;
|
|
27
27
|
const encoder = encoding.createEncoder();
|
|
28
28
|
encoding.writeVarUint(encoder, MSG_SYNC);
|
|
@@ -61,47 +61,51 @@ export class TrysteroProvider {
|
|
|
61
61
|
if (this._destroyed)
|
|
62
62
|
return;
|
|
63
63
|
const room = joinRoom(roomConfig, this.roomName);
|
|
64
|
+
// Re-check after joinRoom — destroy() may have been called during the
|
|
65
|
+
// synchronous joinRoom call or between the destroyed check and here.
|
|
66
|
+
if (this._destroyed) {
|
|
67
|
+
room.leave().catch(() => { });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
64
70
|
this._room = room;
|
|
65
71
|
const [send, receive] = room.makeAction("yjs-sync");
|
|
66
72
|
this._send = send;
|
|
73
|
+
// Trystero action receivers can't be unregistered, so _handleMessage
|
|
74
|
+
// guards against calls after destruction.
|
|
67
75
|
receive((data, peerId) => {
|
|
68
76
|
this._handleMessage(new Uint8Array(data), peerId);
|
|
69
77
|
});
|
|
70
78
|
room.onPeerJoin((peerId) => {
|
|
79
|
+
if (this._destroyed)
|
|
80
|
+
return;
|
|
71
81
|
this._peers.add(peerId);
|
|
72
82
|
this._sendSyncStep1(peerId);
|
|
73
83
|
this._sendAwareness(peerId);
|
|
74
84
|
});
|
|
75
85
|
room.onPeerLeave((peerId) => {
|
|
86
|
+
if (this._destroyed)
|
|
87
|
+
return;
|
|
76
88
|
this._peers.delete(peerId);
|
|
77
|
-
// Remove awareness states that came from this trystero peer
|
|
78
89
|
const clientIds = this._peerClients.get(peerId);
|
|
79
90
|
if (clientIds && clientIds.size > 0) {
|
|
80
91
|
removeAwarenessStates(this.awareness, Array.from(clientIds), "peer left");
|
|
81
|
-
this._peerClients.delete(peerId);
|
|
82
92
|
}
|
|
93
|
+
this._peerClients.delete(peerId);
|
|
83
94
|
});
|
|
84
95
|
this.doc.on("update", this._onDocUpdate);
|
|
96
|
+
// Only broadcast LOCAL awareness changes. Updates received from remote
|
|
97
|
+
// peers (origin === this) don't need re-broadcasting because trystero's
|
|
98
|
+
// send already reaches all peers in the room.
|
|
85
99
|
this.awareness.on("update", ({ added, updated, removed }, origin) => {
|
|
100
|
+
if (this._destroyed || origin === this)
|
|
101
|
+
return;
|
|
86
102
|
const changedClients = [...added, ...updated, ...removed];
|
|
103
|
+
if (changedClients.length === 0)
|
|
104
|
+
return;
|
|
87
105
|
const encoder = encoding.createEncoder();
|
|
88
106
|
encoding.writeVarUint(encoder, MSG_AWARENESS);
|
|
89
107
|
encoding.writeVarUint8Array(encoder, encodeAwarenessUpdate(this.awareness, changedClients));
|
|
90
108
|
this._broadcast(encoding.toUint8Array(encoder));
|
|
91
|
-
// Track which Yjs clientIDs belong to which trystero peer
|
|
92
|
-
if (typeof origin === "string" && origin !== "local") {
|
|
93
|
-
let ids = this._peerClients.get(origin);
|
|
94
|
-
if (!ids) {
|
|
95
|
-
ids = new Set();
|
|
96
|
-
this._peerClients.set(origin, ids);
|
|
97
|
-
}
|
|
98
|
-
for (const id of added)
|
|
99
|
-
ids.add(id);
|
|
100
|
-
for (const id of updated)
|
|
101
|
-
ids.add(id);
|
|
102
|
-
for (const id of removed)
|
|
103
|
-
ids.delete(id);
|
|
104
|
-
}
|
|
105
109
|
});
|
|
106
110
|
}
|
|
107
111
|
_sendSyncStep1(peerId) {
|
|
@@ -119,7 +123,33 @@ export class TrysteroProvider {
|
|
|
119
123
|
encoding.writeVarUint8Array(encoder, encodeAwarenessUpdate(this.awareness, clients));
|
|
120
124
|
this._sendTo(encoding.toUint8Array(encoder), peerId);
|
|
121
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Extract Yjs awareness clientIDs from a raw awareness update binary.
|
|
128
|
+
* Format: [count: varuint] [entries: {clientId: varuint, clock: varuint, state: varstring}...]
|
|
129
|
+
*/
|
|
130
|
+
_trackPeerClients(peerId, update) {
|
|
131
|
+
const decoder = decoding.createDecoder(update);
|
|
132
|
+
const len = decoding.readVarUint(decoder);
|
|
133
|
+
let ids = this._peerClients.get(peerId);
|
|
134
|
+
if (!ids) {
|
|
135
|
+
ids = new Set();
|
|
136
|
+
this._peerClients.set(peerId, ids);
|
|
137
|
+
}
|
|
138
|
+
for (let i = 0; i < len; i++) {
|
|
139
|
+
const clientId = decoding.readVarUint(decoder);
|
|
140
|
+
decoding.readVarUint(decoder); // clock
|
|
141
|
+
const state = decoding.readVarString(decoder);
|
|
142
|
+
if (state === "null") {
|
|
143
|
+
ids.delete(clientId);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
ids.add(clientId);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
122
150
|
_handleMessage(data, peerId) {
|
|
151
|
+
if (this._destroyed)
|
|
152
|
+
return;
|
|
123
153
|
const decoder = decoding.createDecoder(data);
|
|
124
154
|
const msgType = decoding.readVarUint(decoder);
|
|
125
155
|
switch (msgType) {
|
|
@@ -134,7 +164,10 @@ export class TrysteroProvider {
|
|
|
134
164
|
}
|
|
135
165
|
case MSG_AWARENESS: {
|
|
136
166
|
const update = decoding.readVarUint8Array(decoder);
|
|
137
|
-
|
|
167
|
+
this._trackPeerClients(peerId, update);
|
|
168
|
+
// Use `this` as origin (standard y-webrtc pattern) so the
|
|
169
|
+
// awareness "update" handler can skip re-broadcasting.
|
|
170
|
+
applyAwarenessUpdate(this.awareness, update, this);
|
|
138
171
|
break;
|
|
139
172
|
}
|
|
140
173
|
case MSG_LEAVE: {
|
|
@@ -146,19 +179,23 @@ export class TrysteroProvider {
|
|
|
146
179
|
}
|
|
147
180
|
}
|
|
148
181
|
}
|
|
182
|
+
/** Broadcast data to all connected peers. Fire-and-forget with error suppression. */
|
|
149
183
|
_broadcast(data) {
|
|
184
|
+
if (!this._send || this._peers.size === 0)
|
|
185
|
+
return;
|
|
150
186
|
try {
|
|
151
|
-
|
|
152
|
-
this._send(data.buffer);
|
|
153
|
-
}
|
|
187
|
+
this._send(data.buffer)?.catch(() => { });
|
|
154
188
|
}
|
|
155
189
|
catch {
|
|
156
|
-
// Data channel may already be closed
|
|
190
|
+
// Data channel may already be closed
|
|
157
191
|
}
|
|
158
192
|
}
|
|
193
|
+
/** Send data to a specific peer. Fire-and-forget with error suppression. */
|
|
159
194
|
_sendTo(data, peerId) {
|
|
195
|
+
if (!this._send)
|
|
196
|
+
return;
|
|
160
197
|
try {
|
|
161
|
-
this._send
|
|
198
|
+
this._send(data.buffer, peerId)?.catch(() => { });
|
|
162
199
|
}
|
|
163
200
|
catch {
|
|
164
201
|
// Peer's data channel may already be closed
|
|
@@ -168,21 +205,33 @@ export class TrysteroProvider {
|
|
|
168
205
|
if (this._destroyed)
|
|
169
206
|
return;
|
|
170
207
|
this._destroyed = true;
|
|
171
|
-
// Send explicit leave message over data channel
|
|
208
|
+
// Send explicit leave message over data channel before tearing down.
|
|
172
209
|
const encoder = encoding.createEncoder();
|
|
173
210
|
encoding.writeVarUint(encoder, MSG_LEAVE);
|
|
174
211
|
encoding.writeVarUint(encoder, this.doc.clientID);
|
|
175
212
|
this._broadcast(encoding.toUint8Array(encoder));
|
|
176
|
-
//
|
|
213
|
+
// Broadcast awareness removal so peers can clean up immediately.
|
|
177
214
|
removeAwarenessStates(this.awareness, [this.doc.clientID], "provider destroyed");
|
|
178
215
|
this.doc.off("update", this._onDocUpdate);
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
this._room
|
|
216
|
+
// Null out send/peers before awaiting leave — any queued callbacks
|
|
217
|
+
// that fire during teardown will hit the _destroyed guard.
|
|
218
|
+
const room = this._room;
|
|
182
219
|
this._room = null;
|
|
183
220
|
this._send = null;
|
|
184
221
|
this._peers.clear();
|
|
185
222
|
this._peerClients.clear();
|
|
223
|
+
// Clear the awareness check interval to prevent keeping the event loop alive.
|
|
224
|
+
this.awareness.destroy();
|
|
225
|
+
// Await room.leave() — trystero caches rooms in a global `occupiedRooms`
|
|
226
|
+
// map keyed by appId+roomId. The entry is only removed when leave() fully
|
|
227
|
+
// completes (~99ms internal delay). Without awaiting, a subsequent
|
|
228
|
+
// joinRoom() with the same roomId returns the stale room object.
|
|
229
|
+
try {
|
|
230
|
+
await room?.leave();
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Room may already be torn down
|
|
234
|
+
}
|
|
186
235
|
}
|
|
187
236
|
}
|
|
188
237
|
//# sourceMappingURL=trystero-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trystero-provider.js","sourceRoot":"","sources":["../../src/transport/trystero-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACtH,OAAO,KAAK,YAAY,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAI1C,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,SAAS,GAAG,CAAC,CAAC;AAWpB,MAAM,OAAO,gBAAgB;IACnB,GAAG,CAAQ;IACX,SAAS,CAAY;IACrB,QAAQ,CAAS;IACjB,SAAS,CAAgB;IAE1B,KAAK,GAAgB,IAAI,CAAC;IAC1B,KAAK,GAAqC,IAAI,CAAC;IAC/C,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAC3B,UAAU,GAAG,KAAK,CAAC;IACnB,YAAY,CAAgD;IACpE,oFAAoF;IAC5E,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEtD,YAAY,GAAU,EAAE,QAAgB,EAAE,SAAyB,EAAE;QACpE,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;QAEpC,IAAI,CAAC,YAAY,GAAG,CAAC,MAAkB,EAAE,MAAe,EAAE,EAAE;YAC3D,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"trystero-provider.js","sourceRoot":"","sources":["../../src/transport/trystero-provider.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/radio-peer.ts
CHANGED
|
@@ -46,73 +46,78 @@ export class RadioPeer extends TypedEmitter<PeerEventMap> {
|
|
|
46
46
|
private async _init(): Promise<void> {
|
|
47
47
|
this.emit("status", "connecting");
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
try {
|
|
50
|
+
if (this._opts.keypair) {
|
|
51
|
+
this._keypair = this._opts.keypair;
|
|
52
|
+
} else {
|
|
53
|
+
this._keypair = await generateKeypair();
|
|
54
|
+
}
|
|
55
|
+
this._peerId = peerId(this._keypair.publicKey);
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const guard = (pid: string, topic: string) => {
|
|
58
|
+
return this._permissions.canPublish(pid, topic);
|
|
59
|
+
};
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
this._docManager = new DocManager(this._opts.maxSignalsPerChannel, guard);
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
this._permissions = new RoomPermissions(this._docManager.doc);
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
const roomName = `agent-p2p:${this.stationId}`;
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
const transport = this._opts.transport;
|
|
68
|
+
this._provider = new TrysteroProvider(this._docManager.doc, roomName, {
|
|
69
|
+
backend: transport.backend,
|
|
70
|
+
password: this._opts.password || undefined,
|
|
71
|
+
relayUrls: transport.relayUrls,
|
|
72
|
+
});
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
await this._provider.connected;
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
if (this._opts.enablePersistence) {
|
|
77
|
+
try {
|
|
78
|
+
this._idb = createIndexedDBProvider(this._docManager.doc, roomName);
|
|
79
|
+
} catch {
|
|
80
|
+
// IndexedDB not available (e.g. Node.js) — skip
|
|
81
|
+
}
|
|
80
82
|
}
|
|
81
|
-
}
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
setLocalPeer(this._provider.awareness, this._peerId);
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
// TOFU: first peer becomes owner, later peers become members
|
|
87
|
+
if (!this._permissions.isInitialized) {
|
|
88
|
+
this._permissions.initializeAsOwner(this._peerId);
|
|
89
|
+
} else {
|
|
90
|
+
this._permissions.acceptExisting(this._peerId);
|
|
91
|
+
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
// Forward role changes as events
|
|
94
|
+
this._unsubRoles = this._permissions.observeRoles((pid, role) => {
|
|
95
|
+
this.emit("roles:changed", { peerId: pid, role });
|
|
96
|
+
});
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
this._provider.awareness.on("change", () => {
|
|
99
|
+
const current = getConnectedPeers(this._provider.awareness);
|
|
100
|
+
const currentIds = new Set(current.map((p) => p.peerId));
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
for (const p of current) {
|
|
103
|
+
if (!this._knownPeers.has(p.peerId)) {
|
|
104
|
+
this._knownPeers.add(p.peerId);
|
|
105
|
+
this.emit("peer:join", p);
|
|
106
|
+
}
|
|
105
107
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
for (const id of this._knownPeers) {
|
|
109
|
+
if (!currentIds.has(id)) {
|
|
110
|
+
this._knownPeers.delete(id);
|
|
111
|
+
this.emit("peer:leave", { peerId: id, joinedAt: 0 });
|
|
112
|
+
}
|
|
111
113
|
}
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
+
});
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
this.emit("status", "connected");
|
|
117
|
+
} catch (err) {
|
|
118
|
+
this.emit("status", "disconnected");
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
async ready(): Promise<void> {
|
|
@@ -39,7 +39,7 @@ export class TrysteroProvider {
|
|
|
39
39
|
this.awareness = new Awareness(doc);
|
|
40
40
|
|
|
41
41
|
this._onDocUpdate = (update: Uint8Array, origin: unknown) => {
|
|
42
|
-
if (origin === this) return;
|
|
42
|
+
if (this._destroyed || origin === this) return;
|
|
43
43
|
const encoder = encoding.createEncoder();
|
|
44
44
|
encoding.writeVarUint(encoder, MSG_SYNC);
|
|
45
45
|
syncProtocol.writeUpdate(encoder, update);
|
|
@@ -80,53 +80,57 @@ export class TrysteroProvider {
|
|
|
80
80
|
if (this._destroyed) return;
|
|
81
81
|
|
|
82
82
|
const room = joinRoom(roomConfig, this.roomName);
|
|
83
|
+
|
|
84
|
+
// Re-check after joinRoom — destroy() may have been called during the
|
|
85
|
+
// synchronous joinRoom call or between the destroyed check and here.
|
|
86
|
+
if (this._destroyed) {
|
|
87
|
+
room.leave().catch(() => {});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
this._room = room;
|
|
84
92
|
|
|
85
93
|
const [send, receive] = room.makeAction<ArrayBuffer>("yjs-sync");
|
|
86
94
|
this._send = send;
|
|
87
95
|
|
|
96
|
+
// Trystero action receivers can't be unregistered, so _handleMessage
|
|
97
|
+
// guards against calls after destruction.
|
|
88
98
|
(receive as ActionReceiver<ArrayBuffer>)((data: ArrayBuffer, peerId: string) => {
|
|
89
99
|
this._handleMessage(new Uint8Array(data), peerId);
|
|
90
100
|
});
|
|
91
101
|
|
|
92
102
|
room.onPeerJoin((peerId) => {
|
|
103
|
+
if (this._destroyed) return;
|
|
93
104
|
this._peers.add(peerId);
|
|
94
105
|
this._sendSyncStep1(peerId);
|
|
95
106
|
this._sendAwareness(peerId);
|
|
96
107
|
});
|
|
97
108
|
|
|
98
109
|
room.onPeerLeave((peerId) => {
|
|
110
|
+
if (this._destroyed) return;
|
|
99
111
|
this._peers.delete(peerId);
|
|
100
|
-
// Remove awareness states that came from this trystero peer
|
|
101
112
|
const clientIds = this._peerClients.get(peerId);
|
|
102
113
|
if (clientIds && clientIds.size > 0) {
|
|
103
114
|
removeAwarenessStates(this.awareness, Array.from(clientIds), "peer left");
|
|
104
|
-
this._peerClients.delete(peerId);
|
|
105
115
|
}
|
|
116
|
+
this._peerClients.delete(peerId);
|
|
106
117
|
});
|
|
107
118
|
|
|
108
119
|
this.doc.on("update", this._onDocUpdate);
|
|
109
120
|
|
|
121
|
+
// Only broadcast LOCAL awareness changes. Updates received from remote
|
|
122
|
+
// peers (origin === this) don't need re-broadcasting because trystero's
|
|
123
|
+
// send already reaches all peers in the room.
|
|
110
124
|
this.awareness.on(
|
|
111
125
|
"update",
|
|
112
126
|
({ added, updated, removed }: { added: number[]; updated: number[]; removed: number[] }, origin: unknown) => {
|
|
127
|
+
if (this._destroyed || origin === this) return;
|
|
113
128
|
const changedClients = [...added, ...updated, ...removed];
|
|
129
|
+
if (changedClients.length === 0) return;
|
|
114
130
|
const encoder = encoding.createEncoder();
|
|
115
131
|
encoding.writeVarUint(encoder, MSG_AWARENESS);
|
|
116
132
|
encoding.writeVarUint8Array(encoder, encodeAwarenessUpdate(this.awareness, changedClients));
|
|
117
133
|
this._broadcast(encoding.toUint8Array(encoder));
|
|
118
|
-
|
|
119
|
-
// Track which Yjs clientIDs belong to which trystero peer
|
|
120
|
-
if (typeof origin === "string" && origin !== "local") {
|
|
121
|
-
let ids = this._peerClients.get(origin);
|
|
122
|
-
if (!ids) {
|
|
123
|
-
ids = new Set();
|
|
124
|
-
this._peerClients.set(origin, ids);
|
|
125
|
-
}
|
|
126
|
-
for (const id of added) ids.add(id);
|
|
127
|
-
for (const id of updated) ids.add(id);
|
|
128
|
-
for (const id of removed) ids.delete(id);
|
|
129
|
-
}
|
|
130
134
|
},
|
|
131
135
|
);
|
|
132
136
|
}
|
|
@@ -147,7 +151,33 @@ export class TrysteroProvider {
|
|
|
147
151
|
this._sendTo(encoding.toUint8Array(encoder), peerId);
|
|
148
152
|
}
|
|
149
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Extract Yjs awareness clientIDs from a raw awareness update binary.
|
|
156
|
+
* Format: [count: varuint] [entries: {clientId: varuint, clock: varuint, state: varstring}...]
|
|
157
|
+
*/
|
|
158
|
+
private _trackPeerClients(peerId: string, update: Uint8Array): void {
|
|
159
|
+
const decoder = decoding.createDecoder(update);
|
|
160
|
+
const len = decoding.readVarUint(decoder);
|
|
161
|
+
let ids = this._peerClients.get(peerId);
|
|
162
|
+
if (!ids) {
|
|
163
|
+
ids = new Set();
|
|
164
|
+
this._peerClients.set(peerId, ids);
|
|
165
|
+
}
|
|
166
|
+
for (let i = 0; i < len; i++) {
|
|
167
|
+
const clientId = decoding.readVarUint(decoder);
|
|
168
|
+
decoding.readVarUint(decoder); // clock
|
|
169
|
+
const state = decoding.readVarString(decoder);
|
|
170
|
+
if (state === "null") {
|
|
171
|
+
ids.delete(clientId);
|
|
172
|
+
} else {
|
|
173
|
+
ids.add(clientId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
150
178
|
private _handleMessage(data: Uint8Array, peerId: string): void {
|
|
179
|
+
if (this._destroyed) return;
|
|
180
|
+
|
|
151
181
|
const decoder = decoding.createDecoder(data);
|
|
152
182
|
const msgType = decoding.readVarUint(decoder);
|
|
153
183
|
|
|
@@ -163,7 +193,10 @@ export class TrysteroProvider {
|
|
|
163
193
|
}
|
|
164
194
|
case MSG_AWARENESS: {
|
|
165
195
|
const update = decoding.readVarUint8Array(decoder);
|
|
166
|
-
|
|
196
|
+
this._trackPeerClients(peerId, update);
|
|
197
|
+
// Use `this` as origin (standard y-webrtc pattern) so the
|
|
198
|
+
// awareness "update" handler can skip re-broadcasting.
|
|
199
|
+
applyAwarenessUpdate(this.awareness, update, this);
|
|
167
200
|
break;
|
|
168
201
|
}
|
|
169
202
|
case MSG_LEAVE: {
|
|
@@ -176,19 +209,21 @@ export class TrysteroProvider {
|
|
|
176
209
|
}
|
|
177
210
|
}
|
|
178
211
|
|
|
212
|
+
/** Broadcast data to all connected peers. Fire-and-forget with error suppression. */
|
|
179
213
|
private _broadcast(data: Uint8Array): void {
|
|
214
|
+
if (!this._send || this._peers.size === 0) return;
|
|
180
215
|
try {
|
|
181
|
-
|
|
182
|
-
this._send(data.buffer as ArrayBuffer);
|
|
183
|
-
}
|
|
216
|
+
this._send(data.buffer as ArrayBuffer)?.catch(() => {});
|
|
184
217
|
} catch {
|
|
185
|
-
// Data channel may already be closed
|
|
218
|
+
// Data channel may already be closed
|
|
186
219
|
}
|
|
187
220
|
}
|
|
188
221
|
|
|
222
|
+
/** Send data to a specific peer. Fire-and-forget with error suppression. */
|
|
189
223
|
private _sendTo(data: Uint8Array, peerId: string): void {
|
|
224
|
+
if (!this._send) return;
|
|
190
225
|
try {
|
|
191
|
-
this._send
|
|
226
|
+
this._send(data.buffer as ArrayBuffer, peerId)?.catch(() => {});
|
|
192
227
|
} catch {
|
|
193
228
|
// Peer's data channel may already be closed
|
|
194
229
|
}
|
|
@@ -198,23 +233,35 @@ export class TrysteroProvider {
|
|
|
198
233
|
if (this._destroyed) return;
|
|
199
234
|
this._destroyed = true;
|
|
200
235
|
|
|
201
|
-
// Send explicit leave message over data channel
|
|
236
|
+
// Send explicit leave message over data channel before tearing down.
|
|
202
237
|
const encoder = encoding.createEncoder();
|
|
203
238
|
encoding.writeVarUint(encoder, MSG_LEAVE);
|
|
204
239
|
encoding.writeVarUint(encoder, this.doc.clientID);
|
|
205
240
|
this._broadcast(encoding.toUint8Array(encoder));
|
|
206
241
|
|
|
207
|
-
//
|
|
242
|
+
// Broadcast awareness removal so peers can clean up immediately.
|
|
208
243
|
removeAwarenessStates(this.awareness, [this.doc.clientID], "provider destroyed");
|
|
209
244
|
this.doc.off("update", this._onDocUpdate);
|
|
210
245
|
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
this._room?.leave();
|
|
246
|
+
// Null out send/peers before awaiting leave — any queued callbacks
|
|
247
|
+
// that fire during teardown will hit the _destroyed guard.
|
|
248
|
+
const room = this._room;
|
|
215
249
|
this._room = null;
|
|
216
250
|
this._send = null;
|
|
217
251
|
this._peers.clear();
|
|
218
252
|
this._peerClients.clear();
|
|
253
|
+
|
|
254
|
+
// Clear the awareness check interval to prevent keeping the event loop alive.
|
|
255
|
+
this.awareness.destroy();
|
|
256
|
+
|
|
257
|
+
// Await room.leave() — trystero caches rooms in a global `occupiedRooms`
|
|
258
|
+
// map keyed by appId+roomId. The entry is only removed when leave() fully
|
|
259
|
+
// completes (~99ms internal delay). Without awaiting, a subsequent
|
|
260
|
+
// joinRoom() with the same roomId returns the stale room object.
|
|
261
|
+
try {
|
|
262
|
+
await room?.leave();
|
|
263
|
+
} catch {
|
|
264
|
+
// Room may already be torn down
|
|
265
|
+
}
|
|
219
266
|
}
|
|
220
267
|
}
|