egos-transfer 0.2.4 → 0.2.6

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/src/interfaces.ts CHANGED
@@ -188,6 +188,11 @@ export interface MiddlewareItem {
188
188
  errorHandler?: (error: Error) => void;
189
189
  }
190
190
 
191
+ export enum MessageRole {
192
+ Agent = 'agent',
193
+ User = 'user',
194
+ }
195
+
191
196
  export type DataMessage = {
192
197
  id: string;
193
198
  src: string;
@@ -204,6 +209,8 @@ export type DataMessage = {
204
209
  priority?: TransferPriority;
205
210
  retryCount?: number;
206
211
  metadata?: Record<string, any>;
212
+ sessionId?: string;
213
+ role?: MessageRole;
207
214
  };
208
215
 
209
216
  export interface MetaData {
@@ -0,0 +1,671 @@
1
+ import {
2
+ CONNECTION_DUPLICATED,
3
+ CONNECTION_LIMIT_EXCEED,
4
+ CONNECTION_PEER_CONFLICT,
5
+ CONNECTION_TIMEOUT,
6
+ PEER_OPEN_TIMEOUT,
7
+ PING_TIMEOUT,
8
+ UNAUTHORIZED,
9
+ } from './constant';
10
+ import { DataConnection, Peer, PeerOptions } from 'peerjs';
11
+ import {
12
+ IDeviceStatus,
13
+ IMessage,
14
+ PeerMessage,
15
+ PeerRequestPayload,
16
+ RequestCallbackFn,
17
+ RouteItem,
18
+ } from './interfaces';
19
+ import { InternalApi, PeerAction, PeerRoutes, PeerScope, REQUEST_TIMEOUT } from './entity';
20
+
21
+ import { ConnectionManager } from './connection-manager';
22
+ import { PeerRouter } from './router';
23
+ import axios from 'axios';
24
+ import delay from 'delay';
25
+
26
+ export interface ContentPayload {
27
+ file: string;
28
+ parent: string;
29
+ }
30
+
31
+ type ResponseCb<T = any> = (args: T) => void;
32
+ type PeerTransferOptions = PeerOptions & {
33
+ turnExpiredAt: number;
34
+ };
35
+
36
+ const MAX_RECONNECT_BACKOFF = 30000;
37
+ const BASE_RECONNECT_BACKOFF = 1000;
38
+ /** 30 分钟无消息活动则重建 Peer,防止长时间空闲后 signaling 连接僵死 */
39
+ const IDLE_RECREATE_TIMEOUT = 30 * 60 * 1000;
40
+ /** 30 秒未收到服务端 HEARTBEAT 则重连 */
41
+ const HEARTBEAT_TIMEOUT = 30000;
42
+
43
+ export const PEER_CLOSE_RECONNECT_TIMEOUT = 3000;
44
+
45
+ export abstract class PeerTransfer {
46
+ protected peer: Peer | undefined;
47
+ protected connectionManager: ConnectionManager;
48
+ protected peerConfig: PeerTransferOptions | undefined;
49
+ public readonly deviceId: string;
50
+ public readonly peerId: string;
51
+ protected requests = new Map<string, ResponseCb>();
52
+ protected router = new PeerRouter();
53
+ protected reconnectAttempt = 0;
54
+ protected destroyed = false;
55
+
56
+ /** connectionId → Set<requestId> — settle in-flight requests on connection drop */
57
+ private connRequests = new Map<string, Set<string>>();
58
+ private _reconnecting = false;
59
+ /** 退出标志:为 true 时禁止自动重连,直到 createPeer 被调用 */
60
+ private _exited = false;
61
+ /** 空闲重连定时器:超时无消息活动则重建 Peer */
62
+ private idleTimer: ReturnType<typeof setTimeout> | null = null;
63
+ /** 心跳超时定时器:30 秒未收到 HEARTBEAT 则重连 */
64
+ private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;
65
+
66
+ constructor(deviceId: string, config: PeerTransferOptions) {
67
+ this.peerConfig = { ...config, key: String(Date.now()) };
68
+ this.peerId = deviceId;
69
+ this.deviceId = deviceId;
70
+ this.connectionManager = new ConnectionManager({
71
+ getPeer: () => this.peer,
72
+ peerId: this.peerId,
73
+ onConnClosed: (conn) => this.onConnClosed(conn),
74
+ ping: (peerId: string) => this.ping(peerId),
75
+ });
76
+ }
77
+
78
+ abstract getStore(): any;
79
+ abstract online(deviceId: string): void;
80
+ abstract offline(deviceId: string): void;
81
+ abstract genRequestId(): Promise<string>;
82
+ abstract getApi(peerId: string): Promise<InternalApi | undefined>;
83
+ abstract discoverDeviceById(deviceId: string): Promise<void>;
84
+ abstract onDestroy(): void;
85
+ abstract requireAuth(): Promise<void>;
86
+ abstract onConnectLimit(): void;
87
+ abstract reloadConfig(config?: Record<string, any>): Promise<void>;
88
+
89
+ // ── Peer lifecycle ──────────────────────────────────────────────
90
+
91
+ async createPeer(force?: boolean): Promise<Peer> {
92
+ console.info('createPeer##', this.peer?.open, force);
93
+ this._exited = false;
94
+ if (this.peer?.open && !force) {
95
+ return this.peer;
96
+ }
97
+ this.destroyPeer();
98
+ await this.reloadConfig();
99
+
100
+ const peer = new Peer(this.peerId, { ...this.peerConfig, pingInterval: 9000 });
101
+ this.peer = peer;
102
+
103
+ await this.waitForOpen(peer);
104
+ this.destroyed = false;
105
+ peer.on('close', () => {
106
+ setTimeout(() => this.destroyed && this.reconnect(), PEER_CLOSE_RECONNECT_TIMEOUT);
107
+ });
108
+
109
+ peer.on('error', (err) => {
110
+ // 覆盖所有与 signaling server 断连相关的错误类型
111
+ if (
112
+ err.type === 'network' ||
113
+ err.type === 'server-error' ||
114
+ err.type === 'disconnected' ||
115
+ err.type === 'socket-error' ||
116
+ err.type === 'socket-closed'
117
+ ) {
118
+ this.reconnect();
119
+ }
120
+ });
121
+
122
+ peer.on('connection', (conn: DataConnection) => {
123
+ conn.label = 'incoming';
124
+ this.onConnect(conn);
125
+ });
126
+
127
+ const socket = peer.socket;
128
+ socket?.on('message', (message: IMessage) => {
129
+ this.handleSocketMessage(message);
130
+ });
131
+
132
+ // 启动连接健康检查
133
+ this.connectionManager.startHealthCheck();
134
+
135
+ // 启动空闲重连定时器
136
+ this.resetIdleTimer();
137
+
138
+ // 启动心跳超时定时器
139
+ this.resetHeartbeatTimer();
140
+
141
+ return peer;
142
+ }
143
+
144
+ private waitForOpen(peer: Peer): Promise<void> {
145
+ return new Promise<void>((resolve) => {
146
+ const timer = setTimeout(() => resolve(), PEER_OPEN_TIMEOUT);
147
+
148
+ peer.on('open', () => {
149
+ clearTimeout(timer);
150
+ this.reconnectAttempt = 0;
151
+ resolve();
152
+ });
153
+
154
+ peer.once('error', (err) => {
155
+ if (err.message === CONNECTION_DUPLICATED) {
156
+ clearTimeout(timer);
157
+ // signaling server 仍持有旧连接,主动 destroy 以清理服务端状态
158
+ peer.destroy();
159
+ resolve();
160
+ return;
161
+ }
162
+ clearTimeout(timer);
163
+ if (err.message === CONNECTION_PEER_CONFLICT) {
164
+ resolve();
165
+ return;
166
+ }
167
+ if (err.message === CONNECTION_LIMIT_EXCEED) {
168
+ this.onConnectLimit();
169
+ resolve();
170
+ return;
171
+ }
172
+ if (err.message === UNAUTHORIZED) {
173
+ this.requireAuth();
174
+ resolve();
175
+ return;
176
+ }
177
+ resolve();
178
+ });
179
+ });
180
+ }
181
+
182
+ handleSocketMessage(message: IMessage) {
183
+ switch (message.type) {
184
+ case 'ONLINE':
185
+ this.online(message.payload.deviceId);
186
+ break;
187
+ case 'OFFLINE':
188
+ this.ping(message.payload.deviceId);
189
+ break;
190
+ case 'HEARTBEAT':
191
+ this.resetHeartbeatTimer();
192
+ break;
193
+ case 'ERROR':
194
+ if (message.payload?.msg === CONNECTION_PEER_CONFLICT) {
195
+ this.getStore().dispatch({
196
+ type: 'global/updateNetworkState',
197
+ payload: { error: true, code: 'PEER_CONNECTION_CONFLICT', message: 'peer conflict' },
198
+ });
199
+ }
200
+ break;
201
+ }
202
+ }
203
+
204
+ // ── Connection management ───────────────────────────────────────
205
+
206
+ async connectToPeer(peerId: string): Promise<DataConnection | undefined> {
207
+ if (peerId === this.deviceId) return;
208
+
209
+ const existing = this.connectionManager.get(peerId);
210
+ if (existing) {
211
+ return existing;
212
+ }
213
+
214
+ return this.createConnection(peerId);
215
+ }
216
+
217
+ private async createConnection(peerId: string): Promise<DataConnection | undefined> {
218
+ for (let retry = 0; retry < 3; retry++) {
219
+ if (!this.peer?.open) {
220
+ try {
221
+ await this.createPeer();
222
+ } catch {
223
+ /* fall through */
224
+ }
225
+ }
226
+ if (!this.peer?.open) continue;
227
+
228
+ const pooled = this.connectionManager.get(peerId);
229
+ if (pooled) {
230
+ return pooled;
231
+ }
232
+
233
+ const conn = this.peer.connect(peerId, {
234
+ label: 'outgoing',
235
+ metadata: { time: Date.now() },
236
+ });
237
+ if (!conn) continue;
238
+
239
+ const result = await this.onConnect(conn as DataConnection).catch(() => undefined);
240
+ if (result) {
241
+ return result;
242
+ }
243
+
244
+ const pooled2 = this.connectionManager.get(peerId);
245
+ if (pooled2) {
246
+ return pooled2;
247
+ }
248
+ }
249
+ // 所有重试均失败,强制销毁 peer 以确保下次连接使用全新实例
250
+ // (peer.open 可能在 signaling socket 已断连的情况下仍返回 true)
251
+ if (!this.destroyed) {
252
+ this.destroyPeer();
253
+ }
254
+ return undefined;
255
+ }
256
+
257
+ async onConnect(conn: DataConnection): Promise<DataConnection | undefined> {
258
+ if (!conn) return;
259
+
260
+ if (conn.open) {
261
+ this.setupConnection(conn);
262
+ return conn;
263
+ }
264
+
265
+ return new Promise((resolve) => {
266
+ const timer = setTimeout(() => {
267
+ conn.off('open', onOpen);
268
+ conn.off('error', onError);
269
+ conn.off('close', onClose);
270
+ resolve(this.isValidConnection(conn) ? this.setupConnection(conn) : undefined);
271
+ }, CONNECTION_TIMEOUT);
272
+
273
+ const onOpen = () => {
274
+ clearTimeout(timer);
275
+ resolve(this.setupConnection(conn));
276
+ };
277
+
278
+ const onError = () => {
279
+ clearTimeout(timer);
280
+ const pooled = this.connectionManager.get(conn.peer);
281
+ resolve(pooled);
282
+ };
283
+
284
+ const onClose = () => {
285
+ clearTimeout(timer);
286
+ resolve(undefined);
287
+ this.removeConnection(conn.peer, conn.connectionId);
288
+ };
289
+
290
+ conn.once('open', onOpen);
291
+ conn.once('error', onError);
292
+ conn.once('close', onClose);
293
+ });
294
+ }
295
+
296
+ private setupConnection(conn: DataConnection): DataConnection {
297
+ return this.connectionManager.setup(conn, (message: any) => {
298
+ switch (message.scope) {
299
+ case PeerScope.REQUEST:
300
+ return this.onRequest(conn, message);
301
+ case PeerScope.RESPONSE:
302
+ return this.onResponse(conn, message);
303
+ default:
304
+ return this.responseFail(conn, message, 'unknown scope');
305
+ }
306
+ });
307
+ }
308
+
309
+ private onConnClosed(conn: DataConnection) {
310
+ const reqIds = this.connRequests.get(conn.connectionId);
311
+ if (reqIds) {
312
+ // for (const reqId of reqIds) {
313
+ // const cb = this.requests.get(reqId);
314
+ // if (cb) {
315
+ // cb({ error: { message: 'connection closed', code: 'CONN_CLOSED' } } as any);
316
+ // this.requests.delete(reqId);
317
+ // }
318
+ // }
319
+ this.connRequests.delete(conn.connectionId);
320
+ }
321
+ }
322
+
323
+ removeConnection(peerId: string, connectionId?: string) {
324
+ this.connectionManager.remove(peerId, connectionId);
325
+ }
326
+
327
+ isValidConnection(conn?: DataConnection): boolean {
328
+ return this.connectionManager.isValid(conn);
329
+ }
330
+
331
+ // ── Request / Reply ─────────────────────────────────────────────
332
+
333
+ async request(peerId: string, payload: PeerRequestPayload): Promise<PeerMessage> {
334
+ const requestId = payload.requestId || (await this.genRequestId());
335
+ let res: PeerMessage;
336
+ for (let attempt = 0; attempt <= 2; payload.route, attempt++) {
337
+ res = await this.doRequestOnce(peerId, payload, requestId);
338
+ const code = (res.error as Record<string, any>)?.code;
339
+ if (!res.error || (code !== 'CONN_CLOSED' && code !== 'CONN_FAILED')) {
340
+ return res;
341
+ }
342
+ }
343
+ return res!;
344
+ }
345
+
346
+ private async doRequestOnce(
347
+ peerId: string,
348
+ payload: PeerRequestPayload,
349
+ requestId: string,
350
+ ): Promise<PeerMessage> {
351
+ if (this.destroyed) {
352
+ return { ...payload, body: {}, error: { message: 'destroyed', code: 'CONN_CLOSED' } } as any;
353
+ }
354
+
355
+ const conn = await this.connectToPeer(peerId);
356
+ if (!this.isValidConnection(conn)) {
357
+ return {
358
+ ...payload,
359
+ body: {},
360
+ error: { message: 'connection failed', code: 'CONN_FAILED' },
361
+ } as any;
362
+ }
363
+
364
+ return new Promise((resolve) => {
365
+ let timeout: ReturnType<typeof setTimeout> | null = null;
366
+
367
+ const cleanup = () => {
368
+ if (timeout) {
369
+ clearTimeout(timeout);
370
+ timeout = null;
371
+ }
372
+ this.requests.delete(requestId);
373
+ const reqs = this.connRequests.get(conn.connectionId);
374
+ if (reqs) {
375
+ reqs.delete(requestId);
376
+ // connection pool cleanup
377
+ if (reqs.size === 0) {
378
+ this.connRequests.delete(conn.connectionId);
379
+ }
380
+ }
381
+ };
382
+
383
+ timeout = setTimeout(() => {
384
+ cleanup();
385
+ if (!this.isValidConnection(conn)) {
386
+ this.removeConnection(conn.peer, conn.connectionId);
387
+ }
388
+
389
+ resolve({ ...payload, body: {}, error: { message: 'timeout', code: 'TIMEOUT' } } as any);
390
+ }, payload.timeout || REQUEST_TIMEOUT);
391
+
392
+ let reqs = this.connRequests.get(conn.connectionId);
393
+ if (!reqs) {
394
+ reqs = new Set();
395
+ this.connRequests.set(conn.connectionId, reqs);
396
+ }
397
+ reqs.add(requestId);
398
+
399
+ this.requests.set(requestId, (message: Record<string, any>) => {
400
+ cleanup();
401
+ resolve(message as any);
402
+ });
403
+
404
+ this.send(conn, { ...payload, requestId }).catch(() => {
405
+ cleanup();
406
+ resolve({
407
+ ...payload,
408
+ body: {},
409
+ error: { message: 'send failed', code: 'CONN_CLOSED' },
410
+ } as any);
411
+ });
412
+ });
413
+ }
414
+
415
+ async reply(
416
+ conn: DataConnection,
417
+ oldMessage: PeerMessage,
418
+ message: {
419
+ body?: Record<string, any>;
420
+ headers?: Record<string, string | number>;
421
+ error?: string | Record<string, any>;
422
+ },
423
+ ) {
424
+ const data = {
425
+ ...this.inheritMessage(oldMessage),
426
+ ...message,
427
+ dest: conn.peer,
428
+ scope: PeerScope.RESPONSE,
429
+ updatedAt: Date.now(),
430
+ };
431
+ if (!this.isValidConnection(conn)) {
432
+ const c = await this.connectToPeer(conn.peer);
433
+ return c?.send(data);
434
+ }
435
+ return conn.send(data);
436
+ }
437
+
438
+ async send(conn: DataConnection, message: PeerRequestPayload) {
439
+ const internalApi = await this.getApi(conn.peer);
440
+ let connection = conn;
441
+ if (!this.isValidConnection(conn)) {
442
+ const c = await this.connectToPeer(conn.peer);
443
+ if (!c)
444
+ return this.responseFail(conn, message as PeerMessage, 'send with invalid connection');
445
+ connection = c;
446
+ }
447
+ const requestId = message.requestId || (await this.genRequestId());
448
+ return connection.send(
449
+ {
450
+ ...message,
451
+ requestId,
452
+ headers: { ...message.headers, ['x-device-token']: internalApi?.token },
453
+ scope: PeerScope.REQUEST,
454
+ action: PeerAction.SEND,
455
+ src: this.peerId,
456
+ dest: conn.peer,
457
+ createdAt: Date.now(),
458
+ },
459
+ false,
460
+ );
461
+ }
462
+
463
+ // ── Ping ────────────────────────────────────────────────────────
464
+
465
+ async ping(peerId: string): Promise<boolean> {
466
+ if (this.deviceId === peerId) {
467
+ return false;
468
+ }
469
+ const conn = this.connectionManager.get(peerId);
470
+ if (!conn) {
471
+ return false;
472
+ }
473
+ try {
474
+ const requestId = await this.genRequestId();
475
+ return new Promise<boolean>((resolve) => {
476
+ const timer = setTimeout(() => {
477
+ this.requests.delete(requestId);
478
+ resolve(false);
479
+ }, PING_TIMEOUT);
480
+ this.requests.set(requestId, () => {
481
+ clearTimeout(timer);
482
+ resolve(true);
483
+ });
484
+ conn.send({
485
+ route: PeerRoutes.HEARTBEAT,
486
+ method: 'get',
487
+ body: {},
488
+ requestId,
489
+ scope: PeerScope.REQUEST,
490
+ action: PeerAction.SEND,
491
+ src: this.peerId,
492
+ dest: peerId,
493
+ headers: {},
494
+ createdAt: Date.now(),
495
+ });
496
+ });
497
+ } catch {
498
+ return false;
499
+ }
500
+ }
501
+
502
+ // ── Server helpers ──────────────────────────────────────────────
503
+
504
+ getApiHost() {
505
+ const protocol = this.peerConfig?.secure ? 'https' : 'http';
506
+ const host = this.peerConfig?.host || 'localhost';
507
+ const port = this.peerConfig?.port || 9000;
508
+ return `${protocol}://${host}:${port}`;
509
+ }
510
+
511
+ async getDeviceStatus(peerId: string): Promise<IDeviceStatus | undefined> {
512
+ try {
513
+ const res = await axios.get(`${this.getApiHost()}/api/v1/devices/${peerId}/status`);
514
+ if (res.status < 400) return (res.data as { data: IDeviceStatus }).data;
515
+ } catch {
516
+ /* ignore */
517
+ }
518
+ }
519
+
520
+ // ── Reconnection ────────────────────────────────────────────────
521
+
522
+ async reconnect(force?: boolean): Promise<Peer | undefined> {
523
+ if (this._reconnecting || this._exited) return;
524
+ this._reconnecting = true;
525
+ try {
526
+ this.reconnectAttempt++;
527
+ const backoff = Math.min(
528
+ BASE_RECONNECT_BACKOFF * Math.pow(2, this.reconnectAttempt - 1),
529
+ MAX_RECONNECT_BACKOFF,
530
+ );
531
+ await delay(backoff);
532
+ await this.reloadConfig();
533
+ return await this.createPeer(force);
534
+ } catch {
535
+ return this.createPeer(force);
536
+ } finally {
537
+ this._reconnecting = false;
538
+ }
539
+ }
540
+
541
+ // ── Message helpers ─────────────────────────────────────────────
542
+
543
+ inheritMessage(receiveMsg: PeerMessage) {
544
+ return {
545
+ src: this.peerId,
546
+ id: receiveMsg.id,
547
+ requestId: receiveMsg.requestId,
548
+ route: receiveMsg.route,
549
+ createdAt: receiveMsg.createdAt,
550
+ action: PeerAction.RECEIVE,
551
+ };
552
+ }
553
+
554
+ // ── Router integration ──────────────────────────────────────────
555
+
556
+ async onRequest(conn: DataConnection, message: PeerMessage) {
557
+ this.resetIdleTimer();
558
+ try {
559
+ await this.router.run(conn, message);
560
+ } catch (err: any) {
561
+ this.responseFail(conn, message, { message: err.message, code: err.code });
562
+ }
563
+ }
564
+
565
+ async onResponse(_conn: DataConnection, message: PeerMessage) {
566
+ this.resetIdleTimer();
567
+ // 优先检查 health check ping 回调
568
+ const cb = this.requests.get(message.requestId);
569
+ if (cb) {
570
+ cb.apply(this, [message]);
571
+ this.requests.delete(message.requestId);
572
+ }
573
+ }
574
+
575
+ async responseOk(conn: DataConnection, message: PeerMessage) {
576
+ return this.reply(conn, message, { body: { ok: true } });
577
+ }
578
+
579
+ async responseFail(
580
+ conn: DataConnection,
581
+ message: PeerMessage,
582
+ err: string | Record<string, any>,
583
+ ) {
584
+ return this.reply(conn, message, { body: {}, error: err || 'service error' });
585
+ }
586
+
587
+ use(handler: RequestCallbackFn, prefix = '') {
588
+ this.router.use(handler, prefix);
589
+ }
590
+
591
+ addRoute(route: Omit<RouteItem, 'scope'>) {
592
+ this.router.add(route);
593
+ }
594
+
595
+ // ── Lifecycle ───────────────────────────────────────────────────
596
+
597
+ private resetIdleTimer(): void {
598
+ if (this.idleTimer !== null) {
599
+ clearTimeout(this.idleTimer);
600
+ }
601
+ this.idleTimer = setTimeout(() => {
602
+ if (!this.destroyed) {
603
+ this.reconnect(true);
604
+ }
605
+ }, IDLE_RECREATE_TIMEOUT);
606
+ }
607
+
608
+ private clearIdleTimer(): void {
609
+ if (this.idleTimer !== null) {
610
+ clearTimeout(this.idleTimer);
611
+ this.idleTimer = null;
612
+ }
613
+ }
614
+
615
+ private resetHeartbeatTimer(): void {
616
+ if (this.heartbeatTimer !== null) {
617
+ clearTimeout(this.heartbeatTimer);
618
+ }
619
+ this.heartbeatTimer = setTimeout(() => {
620
+ if (!this.destroyed) {
621
+ this.reconnect(true);
622
+ }
623
+ }, HEARTBEAT_TIMEOUT);
624
+ }
625
+
626
+ private clearHeartbeatTimer(): void {
627
+ if (this.heartbeatTimer !== null) {
628
+ clearTimeout(this.heartbeatTimer);
629
+ this.heartbeatTimer = null;
630
+ }
631
+ }
632
+
633
+ clearInstance() {
634
+ this.destroyed = true;
635
+ this.clearIdleTimer();
636
+ this.clearHeartbeatTimer();
637
+
638
+ this.connectionManager.destroy();
639
+
640
+ this.requests.clear();
641
+ this.connRequests.clear();
642
+ this.destroyPeer();
643
+ }
644
+
645
+ destroy() {
646
+ this.clearInstance();
647
+ this.onDestroy();
648
+ }
649
+
650
+ /**
651
+ * 退出:关闭 peer 和所有连接,停止自动重连。
652
+ * 与 destroy stop 后仍可调用 createPeer 恢复工作。
653
+ */
654
+ stop() {
655
+ this._exited = true;
656
+ this.clearIdleTimer();
657
+ this.clearHeartbeatTimer();
658
+ this.connectionManager.stopHealthCheck();
659
+ this.connectionManager.clean();
660
+ this.requests.clear();
661
+ this.connRequests.clear();
662
+ this.destroyPeer();
663
+ this.reconnectAttempt = 0;
664
+ }
665
+
666
+ private destroyPeer() {
667
+ this.peer?.destroy();
668
+ this.peer?.removeAllListeners();
669
+ this.peer = undefined;
670
+ }
671
+ }
package/src/server.ts CHANGED
@@ -4,11 +4,6 @@ import { DataConnection } from 'peerjs';
4
4
  import { PeerRouter } from './router';
5
5
  import { PeerTransfer } from './index';
6
6
 
7
- export interface ContentPayload {
8
- file: string;
9
- parent: string;
10
- }
11
-
12
7
  type ResponseCb<T = any> = (args: T) => void;
13
8
 
14
9
  type SetupServerCB = () => Promise<PeerTransfer>;