peer-client 1.0.0

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1086 -0
  3. package/dist/core/client.d.ts +41 -0
  4. package/dist/core/client.js +361 -0
  5. package/dist/core/emitter.d.ts +11 -0
  6. package/dist/core/emitter.js +46 -0
  7. package/dist/core/identity.d.ts +15 -0
  8. package/dist/core/identity.js +54 -0
  9. package/dist/core/index.d.ts +6 -0
  10. package/dist/core/index.js +6 -0
  11. package/dist/core/peer.d.ts +29 -0
  12. package/dist/core/peer.js +234 -0
  13. package/dist/core/transport.d.ts +35 -0
  14. package/dist/core/transport.js +174 -0
  15. package/dist/core/types.d.ts +173 -0
  16. package/dist/core/types.js +28 -0
  17. package/dist/crypto.d.ts +32 -0
  18. package/dist/crypto.js +168 -0
  19. package/dist/index.d.ts +11 -0
  20. package/dist/index.js +11 -0
  21. package/dist/media.d.ts +63 -0
  22. package/dist/media.js +275 -0
  23. package/dist/react/Audio.d.ts +6 -0
  24. package/dist/react/Audio.js +18 -0
  25. package/dist/react/PeerProvider.d.ts +17 -0
  26. package/dist/react/PeerProvider.js +46 -0
  27. package/dist/react/PeerStatus.d.ts +7 -0
  28. package/dist/react/PeerStatus.js +20 -0
  29. package/dist/react/TransferProgress.d.ts +10 -0
  30. package/dist/react/TransferProgress.js +17 -0
  31. package/dist/react/Video.d.ts +7 -0
  32. package/dist/react/Video.js +18 -0
  33. package/dist/react/index.d.ts +19 -0
  34. package/dist/react/index.js +18 -0
  35. package/dist/react/useBroadcast.d.ts +12 -0
  36. package/dist/react/useBroadcast.js +21 -0
  37. package/dist/react/useCRDT.d.ts +8 -0
  38. package/dist/react/useCRDT.js +37 -0
  39. package/dist/react/useE2E.d.ts +11 -0
  40. package/dist/react/useE2E.js +62 -0
  41. package/dist/react/useFileTransfer.d.ts +24 -0
  42. package/dist/react/useFileTransfer.js +133 -0
  43. package/dist/react/useIdentity.d.ts +9 -0
  44. package/dist/react/useIdentity.js +63 -0
  45. package/dist/react/useMatch.d.ts +11 -0
  46. package/dist/react/useMatch.js +33 -0
  47. package/dist/react/useMedia.d.ts +13 -0
  48. package/dist/react/useMedia.js +89 -0
  49. package/dist/react/useNamespace.d.ts +7 -0
  50. package/dist/react/useNamespace.js +38 -0
  51. package/dist/react/usePeer.d.ts +6 -0
  52. package/dist/react/usePeer.js +49 -0
  53. package/dist/react/usePeerClient.d.ts +7 -0
  54. package/dist/react/usePeerClient.js +5 -0
  55. package/dist/react/useRelay.d.ts +11 -0
  56. package/dist/react/useRelay.js +19 -0
  57. package/dist/react/useRoom.d.ts +17 -0
  58. package/dist/react/useRoom.js +67 -0
  59. package/dist/react/useSync.d.ts +8 -0
  60. package/dist/react/useSync.js +45 -0
  61. package/dist/room.d.ts +44 -0
  62. package/dist/room.js +246 -0
  63. package/dist/sync.d.ts +45 -0
  64. package/dist/sync.js +333 -0
  65. package/dist/transfer.d.ts +49 -0
  66. package/dist/transfer.js +454 -0
  67. package/package.json +76 -0
@@ -0,0 +1,234 @@
1
+ import { Emitter } from './emitter';
2
+ export class Peer extends Emitter {
3
+ fingerprint;
4
+ alias;
5
+ pc;
6
+ channels = new Map();
7
+ pendingCandidates = [];
8
+ remoteDescSet = false;
9
+ _closed = false;
10
+ _sendSignal;
11
+ constructor(fingerprint, alias, iceServers, sendSignal) {
12
+ super();
13
+ this.fingerprint = fingerprint;
14
+ this.alias = alias;
15
+ this._sendSignal = sendSignal;
16
+ this.pc = new RTCPeerConnection({ iceServers });
17
+ this.pc.onicecandidate = (e) => {
18
+ if (e.candidate) {
19
+ this._sendSignal({
20
+ signal_type: 'candidate',
21
+ candidate: JSON.stringify(e.candidate),
22
+ });
23
+ }
24
+ };
25
+ this.pc.onconnectionstatechange = () => {
26
+ if (this._closed)
27
+ return;
28
+ const state = this.pc.connectionState;
29
+ if (state === 'connected')
30
+ this.emit('connected');
31
+ if (state === 'disconnected' || state === 'failed' || state === 'closed') {
32
+ this.emit('disconnected', state);
33
+ }
34
+ };
35
+ this.pc.oniceconnectionstatechange = () => {
36
+ if (this._closed)
37
+ return;
38
+ const state = this.pc.iceConnectionState;
39
+ if (state === 'failed') {
40
+ this.restartIce();
41
+ }
42
+ };
43
+ this.pc.onnegotiationneeded = async () => {
44
+ if (this._closed)
45
+ return;
46
+ try {
47
+ const offer = await this.pc.createOffer();
48
+ await this.pc.setLocalDescription(offer);
49
+ this._sendSignal({ signal_type: 'offer', sdp: offer.sdp });
50
+ }
51
+ catch (e) {
52
+ this.emit('error', e);
53
+ }
54
+ };
55
+ this.pc.ondatachannel = (e) => {
56
+ this.setupChannel(e.channel);
57
+ this.emit('datachannel:create', e.channel);
58
+ };
59
+ this.pc.ontrack = (e) => {
60
+ this.emit('track', e.track, e.streams);
61
+ if (e.streams[0])
62
+ this.emit('stream', e.streams[0]);
63
+ };
64
+ }
65
+ async createOffer(channelConfig) {
66
+ const label = channelConfig?.label ?? 'data';
67
+ const ch = this.pc.createDataChannel(label, {
68
+ ordered: channelConfig?.ordered ?? true,
69
+ maxRetransmits: channelConfig?.maxRetransmits,
70
+ maxPacketLifeTime: channelConfig?.maxPacketLifeTime,
71
+ });
72
+ this.setupChannel(ch);
73
+ const offer = await this.pc.createOffer();
74
+ await this.pc.setLocalDescription(offer);
75
+ this._sendSignal({ signal_type: 'offer', sdp: offer.sdp });
76
+ }
77
+ async handleSignal(payload) {
78
+ if (this._closed)
79
+ return;
80
+ const { signal_type, sdp, candidate } = payload;
81
+ try {
82
+ if (signal_type === 'offer') {
83
+ await this.pc.setRemoteDescription({ type: 'offer', sdp });
84
+ this.remoteDescSet = true;
85
+ await this.flushCandidates();
86
+ const answer = await this.pc.createAnswer();
87
+ await this.pc.setLocalDescription(answer);
88
+ this._sendSignal({ signal_type: 'answer', sdp: answer.sdp });
89
+ }
90
+ else if (signal_type === 'answer') {
91
+ await this.pc.setRemoteDescription({ type: 'answer', sdp });
92
+ this.remoteDescSet = true;
93
+ await this.flushCandidates();
94
+ }
95
+ else if (signal_type === 'candidate') {
96
+ const parsed = JSON.parse(candidate);
97
+ if (this.remoteDescSet) {
98
+ await this.pc.addIceCandidate(parsed);
99
+ }
100
+ else {
101
+ this.pendingCandidates.push(parsed);
102
+ }
103
+ }
104
+ }
105
+ catch (e) {
106
+ this.emit('error', e);
107
+ }
108
+ }
109
+ send(data, channel) {
110
+ const label = channel ?? 'data';
111
+ const ch = this.channels.get(label);
112
+ if (!ch || ch.readyState !== 'open') {
113
+ throw new Error(`Channel "${label}" not open`);
114
+ }
115
+ if (data instanceof ArrayBuffer || data instanceof Uint8Array) {
116
+ ch.send(data);
117
+ }
118
+ else {
119
+ ch.send(typeof data === 'string' ? data : JSON.stringify(data));
120
+ }
121
+ }
122
+ sendBinary(data, channel) {
123
+ const label = channel ?? 'data';
124
+ const ch = this.channels.get(label);
125
+ if (!ch || ch.readyState !== 'open') {
126
+ throw new Error(`Channel "${label}" not open`);
127
+ }
128
+ ch.send(data);
129
+ }
130
+ addStream(stream) {
131
+ stream.getTracks().forEach((track) => {
132
+ this.pc.addTrack(track, stream);
133
+ });
134
+ }
135
+ removeStream(stream) {
136
+ const senders = this.pc.getSenders();
137
+ stream.getTracks().forEach((track) => {
138
+ const sender = senders.find((s) => s.track === track);
139
+ if (sender)
140
+ this.pc.removeTrack(sender);
141
+ });
142
+ }
143
+ createDataChannel(config) {
144
+ const ch = this.pc.createDataChannel(config.label ?? 'extra', {
145
+ ordered: config.ordered ?? true,
146
+ maxRetransmits: config.maxRetransmits,
147
+ maxPacketLifeTime: config.maxPacketLifeTime,
148
+ });
149
+ this.setupChannel(ch);
150
+ return ch;
151
+ }
152
+ getChannel(label) {
153
+ return this.channels.get(label);
154
+ }
155
+ getBufferedAmount(channel) {
156
+ const ch = this.channels.get(channel ?? 'data');
157
+ return ch?.bufferedAmount ?? 0;
158
+ }
159
+ get connectionState() {
160
+ return this._closed ? 'closed' : this.pc.connectionState;
161
+ }
162
+ get channelLabels() {
163
+ return [...this.channels.keys()];
164
+ }
165
+ get closed() {
166
+ return this._closed;
167
+ }
168
+ restartIce() {
169
+ if (this._closed)
170
+ return;
171
+ try {
172
+ this.pc.restartIce();
173
+ }
174
+ catch (e) {
175
+ this.emit('error', e);
176
+ }
177
+ }
178
+ close() {
179
+ if (this._closed)
180
+ return;
181
+ this._closed = true;
182
+ this.channels.forEach((ch) => {
183
+ try {
184
+ ch.close();
185
+ }
186
+ catch { }
187
+ });
188
+ this.channels.clear();
189
+ try {
190
+ this.pc.close();
191
+ }
192
+ catch { }
193
+ this.removeAllListeners();
194
+ }
195
+ setupChannel(ch) {
196
+ this.channels.set(ch.label, ch);
197
+ ch.binaryType = 'arraybuffer';
198
+ ch.onopen = () => {
199
+ this.emit('datachannel:open', ch.label, ch);
200
+ };
201
+ ch.onmessage = (e) => {
202
+ if (e.data instanceof ArrayBuffer) {
203
+ this.emit('data', e.data, ch.label);
204
+ return;
205
+ }
206
+ let parsed;
207
+ try {
208
+ parsed = JSON.parse(e.data);
209
+ }
210
+ catch {
211
+ parsed = e.data;
212
+ }
213
+ this.emit('data', parsed, ch.label);
214
+ };
215
+ ch.onclose = () => {
216
+ this.channels.delete(ch.label);
217
+ this.emit('datachannel:close', ch.label);
218
+ };
219
+ ch.onerror = (e) => {
220
+ this.emit('error', e);
221
+ };
222
+ }
223
+ async flushCandidates() {
224
+ for (const c of this.pendingCandidates) {
225
+ try {
226
+ await this.pc.addIceCandidate(c);
227
+ }
228
+ catch (e) {
229
+ this.emit('error', e);
230
+ }
231
+ }
232
+ this.pendingCandidates = [];
233
+ }
234
+ }
@@ -0,0 +1,35 @@
1
+ import { Emitter } from './emitter';
2
+ import type { ServerMessage } from './types';
3
+ type TransportEvent = 'open' | 'close' | 'message' | 'error' | 'reconnecting';
4
+ export declare class Transport extends Emitter<TransportEvent> {
5
+ private url;
6
+ private autoReconnect;
7
+ private reconnectDelay;
8
+ private reconnectMaxDelay;
9
+ private maxReconnectAttempts;
10
+ private pingInterval;
11
+ private ws;
12
+ private pingTimer;
13
+ private pongTimer;
14
+ private reconnectTimer;
15
+ private reconnectAttempts;
16
+ private intentionalClose;
17
+ private lastPongTime;
18
+ private messageQueue;
19
+ private _connected;
20
+ constructor(url: string, autoReconnect: boolean, reconnectDelay: number, reconnectMaxDelay: number, maxReconnectAttempts: number, pingInterval: number);
21
+ connect(): Promise<void>;
22
+ send(msg: Partial<ServerMessage>): void;
23
+ private sendRaw;
24
+ close(): void;
25
+ get connected(): boolean;
26
+ getQueueSize(): number;
27
+ clearQueue(): void;
28
+ private flushQueue;
29
+ private startPing;
30
+ private schedulePongCheck;
31
+ private stopPing;
32
+ private cleanup;
33
+ private scheduleReconnect;
34
+ }
35
+ export {};
@@ -0,0 +1,174 @@
1
+ import { Emitter } from './emitter';
2
+ import { LIMITS } from './types';
3
+ export class Transport extends Emitter {
4
+ url;
5
+ autoReconnect;
6
+ reconnectDelay;
7
+ reconnectMaxDelay;
8
+ maxReconnectAttempts;
9
+ pingInterval;
10
+ ws = null;
11
+ pingTimer = null;
12
+ pongTimer = null;
13
+ reconnectTimer = null;
14
+ reconnectAttempts = 0;
15
+ intentionalClose = false;
16
+ lastPongTime = 0;
17
+ messageQueue = [];
18
+ _connected = false;
19
+ constructor(url, autoReconnect, reconnectDelay, reconnectMaxDelay, maxReconnectAttempts, pingInterval) {
20
+ super();
21
+ this.url = url;
22
+ this.autoReconnect = autoReconnect;
23
+ this.reconnectDelay = reconnectDelay;
24
+ this.reconnectMaxDelay = reconnectMaxDelay;
25
+ this.maxReconnectAttempts = maxReconnectAttempts;
26
+ this.pingInterval = pingInterval;
27
+ }
28
+ connect() {
29
+ return new Promise((resolve, reject) => {
30
+ this.intentionalClose = false;
31
+ let settled = false;
32
+ try {
33
+ this.ws = new WebSocket(this.url);
34
+ }
35
+ catch (e) {
36
+ reject(e);
37
+ return;
38
+ }
39
+ this.ws.binaryType = 'arraybuffer';
40
+ this.ws.onopen = () => {
41
+ settled = true;
42
+ this._connected = true;
43
+ this.reconnectAttempts = 0;
44
+ this.lastPongTime = Date.now();
45
+ this.startPing();
46
+ this.flushQueue();
47
+ this.emit('open');
48
+ resolve();
49
+ };
50
+ this.ws.onclose = (ev) => {
51
+ this._connected = false;
52
+ this.cleanup();
53
+ this.emit('close', ev.code, ev.reason);
54
+ if (!settled) {
55
+ settled = true;
56
+ reject(new Error(`WebSocket closed: ${ev.code} ${ev.reason}`));
57
+ return;
58
+ }
59
+ if (!this.intentionalClose && this.autoReconnect) {
60
+ this.scheduleReconnect();
61
+ }
62
+ };
63
+ this.ws.onerror = (ev) => {
64
+ this.emit('error', ev);
65
+ };
66
+ this.ws.onmessage = (ev) => {
67
+ try {
68
+ const msg = JSON.parse(ev.data);
69
+ if (msg.type === 'pong') {
70
+ this.lastPongTime = Date.now();
71
+ return;
72
+ }
73
+ if (msg.type === 'ping') {
74
+ this.sendRaw({ type: 'pong' });
75
+ return;
76
+ }
77
+ this.emit('message', msg);
78
+ }
79
+ catch {
80
+ this.emit('error', new Error('Failed to parse message'));
81
+ }
82
+ };
83
+ });
84
+ }
85
+ send(msg) {
86
+ if (this._connected && this.ws?.readyState === WebSocket.OPEN) {
87
+ this.ws.send(JSON.stringify(msg));
88
+ }
89
+ else if (this.messageQueue.length < LIMITS.MESSAGE_QUEUE_MAX) {
90
+ this.messageQueue.push(msg);
91
+ }
92
+ }
93
+ sendRaw(msg) {
94
+ if (this.ws?.readyState === WebSocket.OPEN) {
95
+ this.ws.send(JSON.stringify(msg));
96
+ }
97
+ }
98
+ close() {
99
+ this.intentionalClose = true;
100
+ this._connected = false;
101
+ this.cleanup();
102
+ this.messageQueue = [];
103
+ if (this.ws) {
104
+ this.ws.onclose = null;
105
+ this.ws.onerror = null;
106
+ this.ws.onmessage = null;
107
+ this.ws.close(1000, 'client disconnect');
108
+ this.ws = null;
109
+ }
110
+ }
111
+ get connected() {
112
+ return this._connected && this.ws?.readyState === WebSocket.OPEN;
113
+ }
114
+ getQueueSize() {
115
+ return this.messageQueue.length;
116
+ }
117
+ clearQueue() {
118
+ this.messageQueue = [];
119
+ }
120
+ flushQueue() {
121
+ const queue = this.messageQueue.splice(0);
122
+ for (const msg of queue) {
123
+ this.send(msg);
124
+ }
125
+ }
126
+ startPing() {
127
+ this.stopPing();
128
+ this.pingTimer = setInterval(() => {
129
+ this.sendRaw({ type: 'ping' });
130
+ this.schedulePongCheck();
131
+ }, this.pingInterval);
132
+ }
133
+ schedulePongCheck() {
134
+ if (this.pongTimer)
135
+ clearTimeout(this.pongTimer);
136
+ const timeout = this.pingInterval * LIMITS.PONG_TIMEOUT_MULTIPLIER;
137
+ this.pongTimer = setTimeout(() => {
138
+ const elapsed = Date.now() - this.lastPongTime;
139
+ if (elapsed > timeout) {
140
+ this._connected = false;
141
+ if (this.ws) {
142
+ this.ws.close(4000, 'pong timeout');
143
+ }
144
+ }
145
+ }, timeout);
146
+ }
147
+ stopPing() {
148
+ if (this.pingTimer) {
149
+ clearInterval(this.pingTimer);
150
+ this.pingTimer = null;
151
+ }
152
+ if (this.pongTimer) {
153
+ clearTimeout(this.pongTimer);
154
+ this.pongTimer = null;
155
+ }
156
+ }
157
+ cleanup() {
158
+ this.stopPing();
159
+ if (this.reconnectTimer) {
160
+ clearTimeout(this.reconnectTimer);
161
+ this.reconnectTimer = null;
162
+ }
163
+ }
164
+ scheduleReconnect() {
165
+ if (this.reconnectAttempts >= this.maxReconnectAttempts)
166
+ return;
167
+ const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts), this.reconnectMaxDelay);
168
+ this.reconnectAttempts++;
169
+ this.emit('reconnecting', this.reconnectAttempts, delay);
170
+ this.reconnectTimer = setTimeout(() => {
171
+ this.connect().catch(() => { });
172
+ }, delay);
173
+ }
174
+ }
@@ -0,0 +1,173 @@
1
+ export type MessageType = 'register' | 'registered' | 'join' | 'leave' | 'signal' | 'discover' | 'peer_list' | 'match' | 'matched' | 'relay' | 'broadcast' | 'peer_joined' | 'peer_left' | 'metadata' | 'ping' | 'pong' | 'error' | 'create_room' | 'room_created' | 'join_room' | 'room_info' | 'room_closed' | 'kick';
2
+ export interface ServerMessage {
3
+ type: MessageType;
4
+ from?: string;
5
+ to?: string;
6
+ namespace?: string;
7
+ payload?: any;
8
+ ts?: number;
9
+ }
10
+ export interface PeerInfo {
11
+ fingerprint: string;
12
+ alias: string;
13
+ meta?: Record<string, any>;
14
+ app_type?: string;
15
+ }
16
+ export interface MatchResult {
17
+ namespace: string;
18
+ session_id: string;
19
+ peers: PeerInfo[];
20
+ }
21
+ export interface RoomCreatedResult {
22
+ room_id: string;
23
+ max_size: number;
24
+ owner: string;
25
+ }
26
+ export interface RoomInfoResult {
27
+ room_id: string;
28
+ peer_count: number;
29
+ max_size: number;
30
+ owner: string;
31
+ }
32
+ export interface IdentityKeys {
33
+ fingerprint: string;
34
+ alias: string;
35
+ publicKeyB64: string;
36
+ privateKeyJwk: JsonWebKey;
37
+ publicKeyJwk: JsonWebKey;
38
+ }
39
+ export interface ClientConfig {
40
+ url: string;
41
+ iceServers?: RTCIceServer[];
42
+ alias?: string;
43
+ meta?: Record<string, any>;
44
+ autoReconnect?: boolean;
45
+ reconnectDelay?: number;
46
+ reconnectMaxDelay?: number;
47
+ pingInterval?: number;
48
+ maxReconnectAttempts?: number;
49
+ identityKeys?: IdentityKeys;
50
+ }
51
+ export interface DataChannelConfig {
52
+ label?: string;
53
+ ordered?: boolean;
54
+ maxRetransmits?: number;
55
+ maxPacketLifeTime?: number;
56
+ }
57
+ export interface RoomConfig {
58
+ maxSize?: number;
59
+ }
60
+ export interface MediaConfig {
61
+ audio?: boolean | MediaTrackConstraints;
62
+ video?: boolean | MediaTrackConstraints;
63
+ }
64
+ export interface FileChunk {
65
+ id: string;
66
+ index: number;
67
+ total: number;
68
+ data: ArrayBuffer;
69
+ }
70
+ export interface FileMetadata {
71
+ id: string;
72
+ filename: string;
73
+ size: number;
74
+ mime: string;
75
+ totalChunks: number;
76
+ chunkSize: number;
77
+ }
78
+ export interface TransferOffer {
79
+ _ft: true;
80
+ type: 'offer';
81
+ id: string;
82
+ filename: string;
83
+ size: number;
84
+ mime: string;
85
+ chunkSize: number;
86
+ totalChunks: number;
87
+ }
88
+ export interface TransferAccept {
89
+ _ft: true;
90
+ type: 'accept';
91
+ id: string;
92
+ }
93
+ export interface TransferAck {
94
+ _ft: true;
95
+ type: 'ack';
96
+ id: string;
97
+ index: number;
98
+ }
99
+ export interface TransferComplete {
100
+ _ft: true;
101
+ type: 'complete';
102
+ id: string;
103
+ }
104
+ export interface TransferCancel {
105
+ _ft: true;
106
+ type: 'cancel';
107
+ id: string;
108
+ }
109
+ export interface TransferResume {
110
+ _ft: true;
111
+ type: 'resume';
112
+ id: string;
113
+ lastIndex: number;
114
+ }
115
+ export interface TransferError {
116
+ _ft: true;
117
+ type: 'error';
118
+ id: string;
119
+ message: string;
120
+ }
121
+ export type TransferControl = TransferOffer | TransferAccept | TransferAck | TransferComplete | TransferCancel | TransferResume | TransferError;
122
+ export interface TransferProgress {
123
+ id: string;
124
+ sent: number;
125
+ total: number;
126
+ percentage: number;
127
+ bytesPerSecond?: number;
128
+ }
129
+ export interface TransferState {
130
+ id: string;
131
+ filename: string;
132
+ size: number;
133
+ mime: string;
134
+ totalChunks: number;
135
+ chunkSize: number;
136
+ lastAckedIndex: number;
137
+ }
138
+ export type SyncMode = 'lww' | 'operational' | 'crdt';
139
+ export interface HLC {
140
+ ts: number;
141
+ counter: number;
142
+ node: string;
143
+ }
144
+ export interface SyncConfig {
145
+ mode: SyncMode;
146
+ merge?: (local: any, remote: any) => any;
147
+ }
148
+ export interface SyncState {
149
+ key: string;
150
+ value: any;
151
+ hlc: HLC;
152
+ from: string;
153
+ version: number;
154
+ deleted?: boolean;
155
+ }
156
+ export type PeerClientEvent = 'connected' | 'disconnected' | 'registered' | 'peer_joined' | 'peer_left' | 'peer_list' | 'matched' | 'broadcast' | 'relay' | 'error' | 'reconnecting' | 'reconnected' | 'room_created' | 'room_closed' | 'kicked';
157
+ export type PeerEvent = 'connected' | 'disconnected' | 'data' | 'stream' | 'track' | 'datachannel:create' | 'datachannel:open' | 'datachannel:close' | 'error';
158
+ export declare const DEFAULT_ICE_SERVERS: RTCIceServer[];
159
+ export declare const DEFAULT_CONFIG: Required<Omit<ClientConfig, 'url' | 'identityKeys'>>;
160
+ export declare const LIMITS: {
161
+ readonly MAX_MEDIA_PEERS: 10;
162
+ readonly MAX_DATA_PEERS: 30;
163
+ readonly MAX_GROUP_SIZE: 20;
164
+ readonly RELAY_THRESHOLD: 30;
165
+ readonly CHUNK_SIZE: 65536;
166
+ readonly BUFFERED_AMOUNT_HIGH: number;
167
+ readonly BUFFERED_AMOUNT_LOW: number;
168
+ readonly ACK_INTERVAL: 100;
169
+ readonly TRANSFER_CHANNEL_PREFIX: "ft-";
170
+ readonly TOMBSTONE_TTL: 60000;
171
+ readonly PONG_TIMEOUT_MULTIPLIER: 2.5;
172
+ readonly MESSAGE_QUEUE_MAX: 500;
173
+ };
@@ -0,0 +1,28 @@
1
+ export const DEFAULT_ICE_SERVERS = [
2
+ { urls: 'stun:stun.l.google.com:19302' },
3
+ { urls: 'stun:stun1.l.google.com:19302' },
4
+ ];
5
+ export const DEFAULT_CONFIG = {
6
+ iceServers: DEFAULT_ICE_SERVERS,
7
+ alias: '',
8
+ meta: {},
9
+ autoReconnect: true,
10
+ reconnectDelay: 1000,
11
+ reconnectMaxDelay: 30000,
12
+ pingInterval: 25000,
13
+ maxReconnectAttempts: Infinity,
14
+ };
15
+ export const LIMITS = {
16
+ MAX_MEDIA_PEERS: 10,
17
+ MAX_DATA_PEERS: 30,
18
+ MAX_GROUP_SIZE: 20,
19
+ RELAY_THRESHOLD: 30,
20
+ CHUNK_SIZE: 65536,
21
+ BUFFERED_AMOUNT_HIGH: 4 * 1024 * 1024,
22
+ BUFFERED_AMOUNT_LOW: 1 * 1024 * 1024,
23
+ ACK_INTERVAL: 100,
24
+ TRANSFER_CHANNEL_PREFIX: 'ft-',
25
+ TOMBSTONE_TTL: 60000,
26
+ PONG_TIMEOUT_MULTIPLIER: 2.5,
27
+ MESSAGE_QUEUE_MAX: 500,
28
+ };
@@ -0,0 +1,32 @@
1
+ import { Emitter } from './core/emitter';
2
+ import type { PeerClient } from './core/client';
3
+ import type { Peer } from './core/peer';
4
+ type E2EEvent = 'key_exchanged' | 'error';
5
+ export declare class E2E extends Emitter<E2EEvent> {
6
+ private keys;
7
+ private keyPair;
8
+ private publicKeyRaw;
9
+ init(): Promise<void>;
10
+ getPublicKeyB64(): string;
11
+ getPublicKeyRaw(): ArrayBuffer;
12
+ deriveKey(peerFingerprint: string, remotePublicKeyB64: string): Promise<void>;
13
+ encrypt(peerFingerprint: string, data: string): Promise<string>;
14
+ decrypt(peerFingerprint: string, data: string): Promise<string>;
15
+ hasKey(peerFingerprint: string): boolean;
16
+ removeKey(peerFingerprint: string): void;
17
+ destroy(): void;
18
+ }
19
+ export declare class GroupKeyManager {
20
+ private e2e;
21
+ private client;
22
+ constructor(client: PeerClient);
23
+ getE2E(): E2E;
24
+ init(): Promise<void>;
25
+ exchangeWith(peer: Peer): Promise<void>;
26
+ handleIncomingKeyExchange(peer: Peer, data: any): Promise<void>;
27
+ encryptForPeer(fingerprint: string, data: any): Promise<string>;
28
+ decryptFromPeer(fingerprint: string, data: string): Promise<any>;
29
+ private waitForConnection;
30
+ destroy(): void;
31
+ }
32
+ export {};