@agent-p2p/peer 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.
Files changed (70) hide show
  1. package/dist/events.d.ts +12 -0
  2. package/dist/events.d.ts.map +1 -0
  3. package/dist/events.js +26 -0
  4. package/dist/events.js.map +1 -0
  5. package/dist/index.d.ts +8 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +8 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/permissions/index.d.ts +2 -0
  10. package/dist/permissions/index.d.ts.map +1 -0
  11. package/dist/permissions/index.js +2 -0
  12. package/dist/permissions/index.js.map +1 -0
  13. package/dist/permissions/room-permissions.d.ts +30 -0
  14. package/dist/permissions/room-permissions.d.ts.map +1 -0
  15. package/dist/permissions/room-permissions.js +105 -0
  16. package/dist/permissions/room-permissions.js.map +1 -0
  17. package/dist/radio-peer.d.ts +30 -0
  18. package/dist/radio-peer.d.ts.map +1 -0
  19. package/dist/radio-peer.js +153 -0
  20. package/dist/radio-peer.js.map +1 -0
  21. package/dist/transport/index.d.ts +3 -0
  22. package/dist/transport/index.d.ts.map +1 -0
  23. package/dist/transport/index.js +3 -0
  24. package/dist/transport/index.js.map +1 -0
  25. package/dist/transport/indexeddb.d.ts +4 -0
  26. package/dist/transport/indexeddb.d.ts.map +1 -0
  27. package/dist/transport/indexeddb.js +5 -0
  28. package/dist/transport/indexeddb.js.map +1 -0
  29. package/dist/transport/trystero-provider.d.ts +29 -0
  30. package/dist/transport/trystero-provider.d.ts.map +1 -0
  31. package/dist/transport/trystero-provider.js +141 -0
  32. package/dist/transport/trystero-provider.js.map +1 -0
  33. package/dist/transport/webrtc.d.ts +9 -0
  34. package/dist/transport/webrtc.d.ts.map +1 -0
  35. package/dist/transport/webrtc.js +10 -0
  36. package/dist/transport/webrtc.js.map +1 -0
  37. package/dist/types.d.ts +33 -0
  38. package/dist/types.d.ts.map +1 -0
  39. package/dist/types.js +2 -0
  40. package/dist/types.js.map +1 -0
  41. package/dist/yjs/awareness.d.ts +9 -0
  42. package/dist/yjs/awareness.d.ts.map +1 -0
  43. package/dist/yjs/awareness.js +17 -0
  44. package/dist/yjs/awareness.js.map +1 -0
  45. package/dist/yjs/channel-array.d.ts +16 -0
  46. package/dist/yjs/channel-array.d.ts.map +1 -0
  47. package/dist/yjs/channel-array.js +50 -0
  48. package/dist/yjs/channel-array.js.map +1 -0
  49. package/dist/yjs/doc-manager.d.ts +13 -0
  50. package/dist/yjs/doc-manager.d.ts.map +1 -0
  51. package/dist/yjs/doc-manager.js +30 -0
  52. package/dist/yjs/doc-manager.js.map +1 -0
  53. package/dist/yjs/index.d.ts +4 -0
  54. package/dist/yjs/index.d.ts.map +1 -0
  55. package/dist/yjs/index.js +4 -0
  56. package/dist/yjs/index.js.map +1 -0
  57. package/package.json +31 -0
  58. package/src/events.ts +30 -0
  59. package/src/index.ts +23 -0
  60. package/src/permissions/index.ts +1 -0
  61. package/src/permissions/room-permissions.ts +125 -0
  62. package/src/radio-peer.ts +187 -0
  63. package/src/transport/index.ts +2 -0
  64. package/src/transport/indexeddb.ts +9 -0
  65. package/src/transport/trystero-provider.ts +173 -0
  66. package/src/types.ts +31 -0
  67. package/src/yjs/awareness.ts +25 -0
  68. package/src/yjs/channel-array.ts +60 -0
  69. package/src/yjs/doc-manager.ts +35 -0
  70. package/src/yjs/index.ts +3 -0
@@ -0,0 +1,141 @@
1
+ import { Awareness, applyAwarenessUpdate, encodeAwarenessUpdate, removeAwarenessStates } from "y-protocols/awareness";
2
+ import * as syncProtocol from "y-protocols/sync";
3
+ import * as encoding from "lib0/encoding";
4
+ import * as decoding from "lib0/decoding";
5
+ const MSG_SYNC = 0;
6
+ const MSG_AWARENESS = 1;
7
+ export class TrysteroProvider {
8
+ doc;
9
+ awareness;
10
+ roomName;
11
+ connected;
12
+ _room = null;
13
+ _send = null;
14
+ _peers = new Set();
15
+ _destroyed = false;
16
+ _onDocUpdate;
17
+ constructor(doc, roomName, config = {}) {
18
+ this.doc = doc;
19
+ this.roomName = roomName;
20
+ this.awareness = new Awareness(doc);
21
+ this._onDocUpdate = (update, origin) => {
22
+ if (origin === this)
23
+ return;
24
+ const encoder = encoding.createEncoder();
25
+ encoding.writeVarUint(encoder, MSG_SYNC);
26
+ syncProtocol.writeUpdate(encoder, update);
27
+ this._broadcast(encoding.toUint8Array(encoder));
28
+ };
29
+ this.connected = this._connect(config);
30
+ }
31
+ async _connect(config) {
32
+ const backend = config.backend ?? "torrent";
33
+ let joinRoom;
34
+ switch (backend) {
35
+ case "torrent": {
36
+ const mod = await import("trystero/torrent");
37
+ joinRoom = mod.joinRoom;
38
+ break;
39
+ }
40
+ case "nostr": {
41
+ const mod = await import("trystero/nostr");
42
+ joinRoom = mod.joinRoom;
43
+ break;
44
+ }
45
+ case "mqtt": {
46
+ const mod = await import("trystero/mqtt");
47
+ joinRoom = mod.joinRoom;
48
+ break;
49
+ }
50
+ }
51
+ const roomConfig = {
52
+ appId: config.appId ?? "agent-p2p",
53
+ };
54
+ if (config.password)
55
+ roomConfig.password = config.password;
56
+ if (config.relayUrls)
57
+ roomConfig.relayUrls = config.relayUrls;
58
+ if (this._destroyed)
59
+ return;
60
+ const room = joinRoom(roomConfig, this.roomName);
61
+ this._room = room;
62
+ const [send, receive] = room.makeAction("yjs-sync");
63
+ this._send = send;
64
+ receive((data, peerId) => {
65
+ this._handleMessage(new Uint8Array(data), peerId);
66
+ });
67
+ room.onPeerJoin((peerId) => {
68
+ this._peers.add(peerId);
69
+ this._sendSyncStep1(peerId);
70
+ this._sendAwareness(peerId);
71
+ });
72
+ room.onPeerLeave((peerId) => {
73
+ this._peers.delete(peerId);
74
+ });
75
+ this.doc.on("update", this._onDocUpdate);
76
+ this.awareness.on("update", ({ added, updated, removed }) => {
77
+ const changedClients = [...added, ...updated, ...removed];
78
+ const encoder = encoding.createEncoder();
79
+ encoding.writeVarUint(encoder, MSG_AWARENESS);
80
+ encoding.writeVarUint8Array(encoder, encodeAwarenessUpdate(this.awareness, changedClients));
81
+ this._broadcast(encoding.toUint8Array(encoder));
82
+ });
83
+ }
84
+ _sendSyncStep1(peerId) {
85
+ const encoder = encoding.createEncoder();
86
+ encoding.writeVarUint(encoder, MSG_SYNC);
87
+ syncProtocol.writeSyncStep1(encoder, this.doc);
88
+ this._sendTo(encoding.toUint8Array(encoder), peerId);
89
+ }
90
+ _sendAwareness(peerId) {
91
+ const clients = Array.from(this.awareness.getStates().keys());
92
+ if (clients.length === 0)
93
+ return;
94
+ const encoder = encoding.createEncoder();
95
+ encoding.writeVarUint(encoder, MSG_AWARENESS);
96
+ encoding.writeVarUint8Array(encoder, encodeAwarenessUpdate(this.awareness, clients));
97
+ this._sendTo(encoding.toUint8Array(encoder), peerId);
98
+ }
99
+ _handleMessage(data, peerId) {
100
+ const decoder = decoding.createDecoder(data);
101
+ const msgType = decoding.readVarUint(decoder);
102
+ switch (msgType) {
103
+ case MSG_SYNC: {
104
+ const encoder = encoding.createEncoder();
105
+ encoding.writeVarUint(encoder, MSG_SYNC);
106
+ syncProtocol.readSyncMessage(decoder, encoder, this.doc, this);
107
+ if (encoding.length(encoder) > 1) {
108
+ this._sendTo(encoding.toUint8Array(encoder), peerId);
109
+ }
110
+ break;
111
+ }
112
+ case MSG_AWARENESS: {
113
+ const update = decoding.readVarUint8Array(decoder);
114
+ applyAwarenessUpdate(this.awareness, update, peerId);
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ _broadcast(data) {
120
+ if (this._send && this._peers.size > 0) {
121
+ this._send(data.buffer);
122
+ }
123
+ }
124
+ _sendTo(data, peerId) {
125
+ if (this._send) {
126
+ this._send(data.buffer, peerId);
127
+ }
128
+ }
129
+ destroy() {
130
+ if (this._destroyed)
131
+ return;
132
+ this._destroyed = true;
133
+ removeAwarenessStates(this.awareness, [this.doc.clientID], "provider destroyed");
134
+ this.doc.off("update", this._onDocUpdate);
135
+ this._room?.leave();
136
+ this._room = null;
137
+ this._send = null;
138
+ this._peers.clear();
139
+ }
140
+ }
141
+ //# sourceMappingURL=trystero-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trystero-provider.js","sourceRoot":"","sources":["../../src/transport/trystero-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { WebrtcProvider } from "y-webrtc";
2
+ import type * as Y from "yjs";
3
+ export interface WebRTCTransportOptions {
4
+ signalingServers?: string[];
5
+ password?: string;
6
+ peerOpts?: Record<string, unknown>;
7
+ }
8
+ export declare function createWebRTCProvider(doc: Y.Doc, roomName: string, opts?: WebRTCTransportOptions): WebrtcProvider;
9
+ //# sourceMappingURL=webrtc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../../src/transport/webrtc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,CAAC;AAI9B,MAAM,WAAW,sBAAsB;IACtC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,CAAC,CAAC,GAAG,EACV,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,sBAA2B,GAC/B,cAAc,CAMhB"}
@@ -0,0 +1,10 @@
1
+ import { WebrtcProvider } from "y-webrtc";
2
+ const DEFAULT_SIGNALING = ["ws://localhost:4444", "wss://signaling.yjs.dev", "wss://y-webrtc-signaling-eu.herokuapp.com"];
3
+ export function createWebRTCProvider(doc, roomName, opts = {}) {
4
+ return new WebrtcProvider(roomName, doc, {
5
+ signaling: opts.signalingServers ?? DEFAULT_SIGNALING,
6
+ password: opts.password ?? undefined,
7
+ peerOpts: opts.peerOpts,
8
+ });
9
+ }
10
+ //# sourceMappingURL=webrtc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webrtc.js","sourceRoot":"","sources":["../../src/transport/webrtc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,iBAAiB,GAAG,CAAC,qBAAqB,EAAE,yBAAyB,EAAE,2CAA2C,CAAC,CAAC;AAQ1H,MAAM,UAAU,oBAAoB,CACnC,GAAU,EACV,QAAgB,EAChB,OAA+B,EAAE;IAEjC,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxC,SAAS,EAAE,IAAI,CAAC,gBAAgB,IAAI,iBAAiB;QACrD,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;QACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACvB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { SignalEnvelope, Keypair } from "@agent-p2p/core";
2
+ export type SignalingBackend = "torrent" | "nostr" | "mqtt";
3
+ export interface TransportConfig {
4
+ backend?: SignalingBackend;
5
+ /** Relay/tracker URLs (BitTorrent trackers, Nostr relays, or MQTT brokers) */
6
+ relayUrls?: string[];
7
+ }
8
+ export interface RadioPeerOptions {
9
+ password?: string;
10
+ maxSignalsPerChannel?: number;
11
+ enablePersistence?: boolean;
12
+ keypair?: Keypair;
13
+ transport?: TransportConfig;
14
+ }
15
+ export interface PeerInfo {
16
+ peerId: string;
17
+ joinedAt: number;
18
+ }
19
+ export type PeerEventMap = {
20
+ signal: SignalEnvelope;
21
+ "peer:join": PeerInfo;
22
+ "peer:leave": PeerInfo;
23
+ "permission:denied": {
24
+ topic: string;
25
+ action: "publish" | "read";
26
+ };
27
+ "roles:changed": {
28
+ peerId: string;
29
+ role: string;
30
+ };
31
+ status: "connecting" | "connected" | "disconnected";
32
+ };
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC/B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,QAAQ,CAAC;IACtB,YAAY,EAAE,QAAQ,CAAC;IACvB,mBAAmB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAAA;KAAE,CAAC;IACnE,eAAe,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;CACpD,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import type { PeerInfo } from "../types.js";
2
+ export interface AwarenessLike {
3
+ setLocalStateField(field: string, value: unknown): void;
4
+ getStates(): Map<number, Record<string, unknown>>;
5
+ on(event: string, fn: (...args: unknown[]) => void): void;
6
+ }
7
+ export declare function setLocalPeer(awareness: AwarenessLike, peerId: string): void;
8
+ export declare function getConnectedPeers(awareness: AwarenessLike): PeerInfo[];
9
+ //# sourceMappingURL=awareness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/yjs/awareness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC7B,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACxD,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CAC1D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAK3E;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,aAAa,GAAG,QAAQ,EAAE,CAStE"}
@@ -0,0 +1,17 @@
1
+ export function setLocalPeer(awareness, peerId) {
2
+ awareness.setLocalStateField("peer", {
3
+ peerId,
4
+ joinedAt: Date.now(),
5
+ });
6
+ }
7
+ export function getConnectedPeers(awareness) {
8
+ const peers = [];
9
+ for (const [, state] of awareness.getStates()) {
10
+ const peer = state.peer;
11
+ if (peer) {
12
+ peers.push(peer);
13
+ }
14
+ }
15
+ return peers;
16
+ }
17
+ //# sourceMappingURL=awareness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"awareness.js","sourceRoot":"","sources":["../../src/yjs/awareness.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,YAAY,CAAC,SAAwB,EAAE,MAAc;IACpE,SAAS,CAAC,kBAAkB,CAAC,MAAM,EAAE;QACpC,MAAM;QACN,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;KACpB,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAwB;IACzD,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,SAAS,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAA4B,CAAC;QAChD,IAAI,IAAI,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
@@ -0,0 +1,16 @@
1
+ import * as Y from "yjs";
2
+ import type { SignalEnvelope } from "@agent-p2p/core";
3
+ export type PublishGuard = (peerId: string, topic: string) => boolean;
4
+ export declare class ChannelArray {
5
+ readonly array: Y.Array<SignalEnvelope>;
6
+ readonly topic: string;
7
+ private _maxSignals;
8
+ private _guard?;
9
+ constructor(array: Y.Array<SignalEnvelope>, topic: string, maxSignals?: number, guard?: PublishGuard);
10
+ push(signal: SignalEnvelope): void;
11
+ getAll(): SignalEnvelope[];
12
+ get length(): number;
13
+ observe(fn: (signals: SignalEnvelope[]) => void, filter?: (signal: SignalEnvelope) => boolean): () => void;
14
+ private trim;
15
+ }
16
+ //# sourceMappingURL=channel-array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-array.d.ts","sourceRoot":"","sources":["../../src/yjs/channel-array.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAEtE,qBAAa,YAAY;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAC,CAAe;gBAElB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,SAAO,EAAE,KAAK,CAAC,EAAE,YAAY;IAOlG,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAQlC,MAAM,IAAI,cAAc,EAAE;IAI1B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,GAAG,MAAM,IAAI;IAmB1G,OAAO,CAAC,IAAI;CAMZ"}
@@ -0,0 +1,50 @@
1
+ export class ChannelArray {
2
+ array;
3
+ topic;
4
+ _maxSignals;
5
+ _guard;
6
+ constructor(array, topic, maxSignals = 1000, guard) {
7
+ this.array = array;
8
+ this.topic = topic;
9
+ this._maxSignals = maxSignals;
10
+ this._guard = guard;
11
+ }
12
+ push(signal) {
13
+ if (this._guard && !this._guard(signal.provenance.peer_id, this.topic)) {
14
+ throw new Error(`Permission denied: cannot publish to "${this.topic}"`);
15
+ }
16
+ this.array.push([signal]);
17
+ this.trim();
18
+ }
19
+ getAll() {
20
+ return this.array.toArray();
21
+ }
22
+ get length() {
23
+ return this.array.length;
24
+ }
25
+ observe(fn, filter) {
26
+ const handler = (event) => {
27
+ const added = [];
28
+ for (const item of event.changes.added) {
29
+ for (const content of item.content.getContent()) {
30
+ const signal = content;
31
+ if (!filter || filter(signal)) {
32
+ added.push(signal);
33
+ }
34
+ }
35
+ }
36
+ if (added.length > 0) {
37
+ fn(added);
38
+ }
39
+ };
40
+ this.array.observe(handler);
41
+ return () => this.array.unobserve(handler);
42
+ }
43
+ trim() {
44
+ if (this.array.length > this._maxSignals) {
45
+ const excess = this.array.length - this._maxSignals;
46
+ this.array.delete(0, excess);
47
+ }
48
+ }
49
+ }
50
+ //# sourceMappingURL=channel-array.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-array.js","sourceRoot":"","sources":["../../src/yjs/channel-array.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,YAAY;IACf,KAAK,CAA0B;IAC/B,KAAK,CAAS;IACf,WAAW,CAAS;IACpB,MAAM,CAAgB;IAE9B,YAAY,KAA8B,EAAE,KAAa,EAAE,UAAU,GAAG,IAAI,EAAE,KAAoB;QACjG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,CAAC,MAAsB;QAC1B,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;IACb,CAAC;IAED,MAAM;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED,OAAO,CAAC,EAAuC,EAAE,MAA4C;QAC5F,MAAM,OAAO,GAAG,CAAC,KAAoC,EAAE,EAAE;YACxD,MAAM,KAAK,GAAqB,EAAE,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACxC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;oBACjD,MAAM,MAAM,GAAG,OAAyB,CAAC;oBACzC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;wBAC/B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACpB,CAAC;gBACF,CAAC;YACF,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,EAAE,CAAC,KAAK,CAAC,CAAC;YACX,CAAC;QACF,CAAC,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAEO,IAAI;QACX,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9B,CAAC;IACF,CAAC;CACD"}
@@ -0,0 +1,13 @@
1
+ import * as Y from "yjs";
2
+ import { ChannelArray, type PublishGuard } from "./channel-array.js";
3
+ export declare class DocManager {
4
+ readonly doc: Y.Doc;
5
+ private _channels;
6
+ private _maxSignals;
7
+ private _guard?;
8
+ constructor(maxSignals?: number, guard?: PublishGuard);
9
+ getChannel(topic: string): ChannelArray;
10
+ getTopics(): string[];
11
+ destroy(): void;
12
+ }
13
+ //# sourceMappingURL=doc-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc-manager.d.ts","sourceRoot":"","sources":["../../src/yjs/doc-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAErE,qBAAa,UAAU;IACtB,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAC,CAAe;gBAElB,UAAU,SAAO,EAAE,KAAK,CAAC,EAAE,YAAY;IAMnD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY;IAUvC,SAAS,IAAI,MAAM,EAAE;IAIrB,OAAO,IAAI,IAAI;CAIf"}
@@ -0,0 +1,30 @@
1
+ import * as Y from "yjs";
2
+ import { ChannelArray } from "./channel-array.js";
3
+ export class DocManager {
4
+ doc;
5
+ _channels = new Map();
6
+ _maxSignals;
7
+ _guard;
8
+ constructor(maxSignals = 1000, guard) {
9
+ this.doc = new Y.Doc();
10
+ this._maxSignals = maxSignals;
11
+ this._guard = guard;
12
+ }
13
+ getChannel(topic) {
14
+ let ch = this._channels.get(topic);
15
+ if (!ch) {
16
+ const arr = this.doc.getArray(`ch:${topic}`);
17
+ ch = new ChannelArray(arr, topic, this._maxSignals, this._guard);
18
+ this._channels.set(topic, ch);
19
+ }
20
+ return ch;
21
+ }
22
+ getTopics() {
23
+ return Array.from(this._channels.keys());
24
+ }
25
+ destroy() {
26
+ this._channels.clear();
27
+ this.doc.destroy();
28
+ }
29
+ }
30
+ //# sourceMappingURL=doc-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc-manager.js","sourceRoot":"","sources":["../../src/yjs/doc-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAqB,MAAM,oBAAoB,CAAC;AAErE,MAAM,OAAO,UAAU;IACb,GAAG,CAAQ;IACZ,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC5C,WAAW,CAAS;IACpB,MAAM,CAAgB;IAE9B,YAAY,UAAU,GAAG,IAAI,EAAE,KAAoB;QAClD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,UAAU,CAAC,KAAa;QACvB,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAiB,MAAM,KAAK,EAAE,CAAC,CAAC;YAC7D,EAAE,GAAG,IAAI,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YACjE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,EAAE,CAAC;IACX,CAAC;IAED,SAAS;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO;QACN,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC;CACD"}
@@ -0,0 +1,4 @@
1
+ export { DocManager } from "./doc-manager.js";
2
+ export { ChannelArray, type PublishGuard } from "./channel-array.js";
3
+ export { setLocalPeer, getConnectedPeers } from "./awareness.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/yjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { DocManager } from "./doc-manager.js";
2
+ export { ChannelArray } from "./channel-array.js";
3
+ export { setLocalPeer, getConnectedPeers } from "./awareness.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/yjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAqB,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@agent-p2p/peer",
3
+ "version": "0.0.1",
4
+ "description": "P2P peer transport for agent-p2p — Yjs + trystero (serverless WebRTC)",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "bun": "./src/index.ts",
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "files": ["src", "dist"],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json"
16
+ },
17
+ "dependencies": {
18
+ "@agent-p2p/core": "^0.0.1",
19
+ "yjs": "^13.6",
20
+ "y-protocols": "^1.0",
21
+ "y-indexeddb": "^9.0",
22
+ "lib0": "^0.2",
23
+ "trystero": "^0.20"
24
+ },
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/luisfreitas/radio.ag",
29
+ "directory": "packages/peer"
30
+ }
31
+ }
package/src/events.ts ADDED
@@ -0,0 +1,30 @@
1
+ type Listener<T> = (data: T) => void;
2
+
3
+ export class TypedEmitter<EventMap extends { [key: string]: unknown }> {
4
+ private _listeners = new Map<keyof EventMap, Set<Listener<never>>>();
5
+
6
+ on<K extends keyof EventMap>(event: K, fn: Listener<EventMap[K]>): () => void {
7
+ if (!this._listeners.has(event)) {
8
+ this._listeners.set(event, new Set());
9
+ }
10
+ const set = this._listeners.get(event)!;
11
+ set.add(fn as Listener<never>);
12
+ return () => set.delete(fn as Listener<never>);
13
+ }
14
+
15
+ off<K extends keyof EventMap>(event: K, fn: Listener<EventMap[K]>): void {
16
+ this._listeners.get(event)?.delete(fn as Listener<never>);
17
+ }
18
+
19
+ protected emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {
20
+ const set = this._listeners.get(event);
21
+ if (!set) return;
22
+ for (const fn of set) {
23
+ (fn as Listener<EventMap[K]>)(data);
24
+ }
25
+ }
26
+
27
+ removeAllListeners(): void {
28
+ this._listeners.clear();
29
+ }
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export { RadioPeer } from "./radio-peer.js";
2
+ export { TypedEmitter } from "./events.js";
3
+ export { RoomPermissions } from "./permissions/index.js";
4
+ export type { RadioPeerOptions, PeerInfo, PeerEventMap, SignalingBackend, TransportConfig } from "./types.js";
5
+ export { TrysteroProvider, type TrysteroConfig } from "./transport/trystero-provider.js";
6
+ export { DocManager, ChannelArray, type PublishGuard } from "./yjs/index.js";
7
+
8
+ // Re-export core essentials
9
+ export {
10
+ type SignalEnvelope,
11
+ type Keypair,
12
+ type RoomRole,
13
+ type ChannelPolicy,
14
+ type RoomConfig,
15
+ ROLE_WEIGHT,
16
+ SignalBuilder,
17
+ generateKeypair,
18
+ peerId,
19
+ exportKeypair,
20
+ importKeypair,
21
+ signSignal,
22
+ verifySignal,
23
+ } from "@agent-p2p/core";
@@ -0,0 +1 @@
1
+ export { RoomPermissions } from "./room-permissions.js";
@@ -0,0 +1,125 @@
1
+ import * as Y from "yjs";
2
+ import { type RoomRole, type ChannelPolicy, ROLE_WEIGHT } from "@agent-p2p/core";
3
+
4
+ export interface RoomPermissionsEvents {
5
+ "roles:changed": { peerId: string; role: RoomRole };
6
+ "config:changed": void;
7
+ }
8
+
9
+ export class RoomPermissions {
10
+ private _configMap: Y.Map<unknown>;
11
+ private _rolesMap: Y.Map<RoomRole>;
12
+
13
+ constructor(doc: Y.Doc) {
14
+ this._configMap = doc.getMap("room:config");
15
+ this._rolesMap = doc.getMap("room:roles");
16
+ }
17
+
18
+ get ownerId(): string | undefined {
19
+ return this._configMap.get("owner_id") as string | undefined;
20
+ }
21
+
22
+ get isInitialized(): boolean {
23
+ return this._configMap.has("owner_id");
24
+ }
25
+
26
+ initializeAsOwner(peerId: string): void {
27
+ if (this.isInitialized) return;
28
+ this._configMap.doc!.transact(() => {
29
+ this._configMap.set("owner_id", peerId);
30
+ this._configMap.set("created_at", new Date().toISOString());
31
+ this._configMap.set("default_publish_role", "member");
32
+ this._configMap.set("default_read_role", "viewer");
33
+ this._configMap.set("channel_policies", {});
34
+ this._rolesMap.set(peerId, "owner");
35
+ });
36
+ }
37
+
38
+ acceptExisting(peerId: string): void {
39
+ if (!this._rolesMap.has(peerId)) {
40
+ this._rolesMap.set(peerId, "member");
41
+ }
42
+ }
43
+
44
+ getRole(peerId: string): RoomRole {
45
+ return this._rolesMap.get(peerId) ?? "viewer";
46
+ }
47
+
48
+ private _getRoleWeight(peerId: string): number {
49
+ return ROLE_WEIGHT[this.getRole(peerId)];
50
+ }
51
+
52
+ private _getMinPublishRole(topic: string): RoomRole {
53
+ const policies = this._configMap.get("channel_policies") as Record<string, ChannelPolicy> | undefined;
54
+ return policies?.[topic]?.min_publish_role ?? (this._configMap.get("default_publish_role") as RoomRole) ?? "member";
55
+ }
56
+
57
+ private _getMinReadRole(topic: string): RoomRole {
58
+ const policies = this._configMap.get("channel_policies") as Record<string, ChannelPolicy> | undefined;
59
+ return policies?.[topic]?.min_read_role ?? (this._configMap.get("default_read_role") as RoomRole) ?? "viewer";
60
+ }
61
+
62
+ canPublish(peerId: string, topic: string): boolean {
63
+ return this._getRoleWeight(peerId) >= ROLE_WEIGHT[this._getMinPublishRole(topic)];
64
+ }
65
+
66
+ canRead(peerId: string, topic: string): boolean {
67
+ return this._getRoleWeight(peerId) >= ROLE_WEIGHT[this._getMinReadRole(topic)];
68
+ }
69
+
70
+ promotePeer(actorId: string, targetId: string, newRole: RoomRole): boolean {
71
+ const actorWeight = this._getRoleWeight(actorId);
72
+ const targetWeight = ROLE_WEIGHT[newRole];
73
+
74
+ // Must be at least moderator to promote
75
+ if (actorWeight < ROLE_WEIGHT.moderator) return false;
76
+ // Cannot promote above own role
77
+ if (targetWeight > actorWeight) return false;
78
+ // Cannot change the owner's role
79
+ if (targetId === this.ownerId && actorId !== this.ownerId) return false;
80
+
81
+ this._rolesMap.set(targetId, newRole);
82
+ return true;
83
+ }
84
+
85
+ setChannelPolicy(actorId: string, topic: string, policy: Partial<Pick<ChannelPolicy, "min_publish_role" | "min_read_role">>): boolean {
86
+ if (this._getRoleWeight(actorId) < ROLE_WEIGHT.moderator) return false;
87
+
88
+ const policies = (this._configMap.get("channel_policies") as Record<string, ChannelPolicy>) ?? {};
89
+ const existing = policies[topic];
90
+ policies[topic] = {
91
+ topic,
92
+ min_publish_role: existing?.min_publish_role ?? "member",
93
+ min_read_role: existing?.min_read_role ?? "viewer",
94
+ ...policy,
95
+ };
96
+ this._configMap.set("channel_policies", policies);
97
+ return true;
98
+ }
99
+
100
+ observeRoles(fn: (peerId: string, role: RoomRole) => void): () => void {
101
+ const handler = (event: Y.YMapEvent<RoomRole>) => {
102
+ for (const [key, change] of event.changes.keys) {
103
+ if (change.action === "add" || change.action === "update") {
104
+ fn(key, this._rolesMap.get(key)!);
105
+ }
106
+ }
107
+ };
108
+ this._rolesMap.observe(handler);
109
+ return () => this._rolesMap.unobserve(handler);
110
+ }
111
+
112
+ observeConfig(fn: () => void): () => void {
113
+ const handler = () => fn();
114
+ this._configMap.observe(handler);
115
+ return () => this._configMap.unobserve(handler);
116
+ }
117
+
118
+ getAllRoles(): Map<string, RoomRole> {
119
+ const result = new Map<string, RoomRole>();
120
+ for (const [key, value] of this._rolesMap.entries()) {
121
+ result.set(key, value);
122
+ }
123
+ return result;
124
+ }
125
+ }