egos-transfer 0.2.2 → 0.2.4

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/index.js CHANGED
@@ -33,22 +33,32 @@ const entity_1 = require("./entity");
33
33
  const router_1 = require("./router");
34
34
  const axios_1 = __importDefault(require("axios"));
35
35
  const delay_1 = __importDefault(require("delay"));
36
+ /** 重连退避相关常量 */
37
+ const MAX_RECONNECT_BACKOFF = 30000; // 最大退避 30s
38
+ const BASE_RECONNECT_BACKOFF = 1000; // 基础退避 1s
36
39
  class PeerTransfer {
37
- constructor(deviceId, config, isRadomPeerId = false) {
38
- this.retry = 0;
40
+ constructor(deviceId, config, isRandomPeerId = false) {
41
+ this.checkerId = null;
42
+ /** 重连尝试次数,成功连接后归零 */
43
+ this.reconnectAttempt = 0;
44
+ /** 标记实例已销毁,防止异步操作继续 */
45
+ this.destroyed = false;
46
+ /** 按 requestId 追踪进行中的请求 */
39
47
  this.inRequest = new Map();
40
48
  this.connectPool = new Map();
41
49
  this.peerConfig = Object.assign(Object.assign({}, config), { key: String(Date.now()) });
42
50
  this.requests = new Map();
51
+ this.requestTimers = new Map();
43
52
  this.timeout = entity_1.REQUEST_TIMEOUT;
44
53
  this.router = new router_1.PeerRouter();
45
54
  this.isPaused = false;
46
- this.peerId = isRadomPeerId ? deviceId + '_' + Date.now() : deviceId;
55
+ this.peerId = isRandomPeerId ? deviceId + '_' + Date.now() : deviceId;
47
56
  this.deviceId = deviceId;
48
57
  }
49
58
  createPeer() {
50
59
  return __awaiter(this, arguments, void 0, function* (force = false) {
51
60
  var _a;
61
+ console.log('createPeer', this.isPaused, force);
52
62
  if (this.isPaused && !force) {
53
63
  return;
54
64
  }
@@ -65,82 +75,90 @@ class PeerTransfer {
65
75
  this.peer = peer;
66
76
  yield this.initialize(this.peer);
67
77
  this.checkConnection();
78
+ this.destroyed = false;
68
79
  return this.peer;
69
80
  });
70
81
  }
71
82
  initialize(peer) {
72
83
  return __awaiter(this, void 0, void 0, function* () {
73
- try {
74
- if (this.checkerId) {
75
- clearInterval(this.checkerId);
76
- }
77
- if (this.booted) {
78
- return this.booted;
79
- }
80
- this.booted = new Promise((resolve) => {
81
- const timer = setTimeout(() => {
82
- resolve(null);
83
- }, constant_1.PEER_OPEN_TIMEOUT);
84
- const done = (peer) => {
85
- clearTimeout(timer);
86
- resolve(peer);
87
- };
88
- peer.on('open', (id) => {
89
- console.log('peer@open', Date.now());
90
- clearTimeout(timer);
91
- done(peer);
92
- });
93
- peer.on('disconnected', () => {
94
- if (!this.isPaused) {
95
- setTimeout(() => {
96
- if (!this.isPaused) {
97
- this.reconnect();
98
- }
99
- }, constant_1.CONNECTION_RETRY);
100
- }
101
- });
102
- // duplicated with close event
103
- peer.on('close', (...args) => { });
104
- peer.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
105
- if (err.message === constant_1.CONNECTION_PEER_CONFLICT) {
106
- this.stop();
107
- done(null);
108
- return;
109
- }
110
- if (err.message === constant_1.CONNECTION_LIMIT_EXCEED) {
111
- this.stop();
112
- this.onConnectLimit();
113
- return done(null);
114
- }
115
- if (err.message == constant_1.UNAUTHORIZED) {
116
- done(null);
117
- return this.requireAuth();
118
- }
119
- if (err.type === 'network') {
120
- done(null);
121
- return this.reconnect();
122
- }
123
- if (err.type === 'server-error') {
124
- done(null);
125
- return this.reconnect();
126
- }
127
- done(null);
128
- return this.createPeer(true);
129
- }));
130
- });
131
- peer.on('connection', (conn) => {
132
- conn.label = 'incoming';
133
- this.onConnect(conn);
84
+ if (this.checkerId) {
85
+ clearInterval(this.checkerId);
86
+ this.checkerId = null;
87
+ }
88
+ if (this.booted) {
89
+ return this.booted;
90
+ }
91
+ this.booted = new Promise((resolve) => {
92
+ const timer = setTimeout(() => {
93
+ resolve(null);
94
+ }, constant_1.PEER_OPEN_TIMEOUT);
95
+ const done = (p) => {
96
+ clearTimeout(timer);
97
+ resolve(p);
98
+ };
99
+ peer.on('open', (id) => {
100
+ console.log('peer@open');
101
+ this.reconnectAttempt = 0; // 连接成功,重置退避计数
102
+ done(peer);
134
103
  });
135
- const socket = peer.socket;
136
- socket === null || socket === void 0 ? void 0 : socket.on('message', (message) => {
137
- this.handleSocketMessage(message);
104
+ // peer.on('disconnected', () => {
105
+ // if (!this.isPaused) {
106
+ // this.reconnect();
107
+ // }
108
+ // });
109
+ peer.on('close', () => {
110
+ setTimeout(() => {
111
+ if (!this.isPaused && !this.destroyed) {
112
+ this.reconnect();
113
+ }
114
+ }, constant_1.CONNECTION_TIMEOUT);
138
115
  });
139
- this.onHeartbeat({ type: 'HEARTBEAT' });
116
+ peer.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
117
+ if (err.message === constant_1.CONNECTION_PEER_CONFLICT) {
118
+ this.stop();
119
+ done(null);
120
+ return;
121
+ }
122
+ if (err.message === constant_1.CONNECTION_LIMIT_EXCEED) {
123
+ this.onConnectLimit();
124
+ return done(null);
125
+ }
126
+ if (err.message === constant_1.UNAUTHORIZED) {
127
+ done(null);
128
+ return this.requireAuth();
129
+ }
130
+ if (err.message === constant_1.CONNECTION_DUPLICATED) {
131
+ // 连接级重复错误,不应销毁重建 peer
132
+ // createConnection 重试循环会处理
133
+ console.warn('peer@error: connection duplicated, will retry');
134
+ return;
135
+ }
136
+ if (err.type === 'network') {
137
+ done(null);
138
+ return this.reconnect();
139
+ }
140
+ if (err.type === 'server-error') {
141
+ done(null);
142
+ return this.reconnect();
143
+ }
144
+ done(null);
145
+ return this.createPeer(true);
146
+ }));
147
+ });
148
+ peer.on('connection', (conn) => {
149
+ conn.label = 'incoming';
150
+ this.onConnect(conn);
151
+ });
152
+ const socket = peer.socket;
153
+ socket === null || socket === void 0 ? void 0 : socket.on('message', (message) => {
154
+ this.handleSocketMessage(message);
155
+ });
156
+ this.onHeartbeat({ type: 'HEARTBEAT' });
157
+ try {
140
158
  yield this.booted;
141
159
  }
142
- catch (err) {
143
- this.booted = undefined;
160
+ catch (_a) {
161
+ // booted rejected via timer timeout
144
162
  }
145
163
  finally {
146
164
  this.booted = undefined;
@@ -158,10 +176,7 @@ class PeerTransfer {
158
176
  this.onHeartbeat(message);
159
177
  }
160
178
  onHeartbeat(message) {
161
- if (message.type !== 'HEARTBEAT') {
162
- return;
163
- }
164
- if (this.isPaused) {
179
+ if (message.type !== 'HEARTBEAT' || this.isPaused) {
165
180
  return;
166
181
  }
167
182
  }
@@ -180,7 +195,6 @@ class PeerTransfer {
180
195
  message: 'peer conflict',
181
196
  },
182
197
  });
183
- return;
184
198
  }
185
199
  }
186
200
  onOpen(message) {
@@ -193,32 +207,24 @@ class PeerTransfer {
193
207
  if (message.type !== 'ONLINE') {
194
208
  return;
195
209
  }
196
- const deviceId = message.payload.deviceId;
197
- this.online(deviceId);
210
+ this.online(message.payload.deviceId);
198
211
  });
199
212
  }
200
213
  onDeviceOffline(message) {
201
214
  if (message.type !== 'OFFLINE') {
202
215
  return;
203
216
  }
204
- const deviceId = message.payload.deviceId;
205
- this.ping(deviceId);
217
+ this.ping(message.payload.deviceId);
206
218
  }
207
219
  removeConnection(peerId, connectionId) {
208
220
  var _a, _b, _c;
209
- // console.trace('removeConnection@', peerId, connectionId);
210
221
  const conn = this.connectPool.get(peerId);
211
222
  if (conn) {
212
223
  if (connectionId && conn.connectionId !== connectionId) {
213
224
  return;
214
225
  }
215
- const sending = this.inRequest.get(conn.connectionId);
216
- if (sending) {
217
- // 提前结束request,避免关闭连接时触发反馈
218
- sending.feedback();
219
- this.inRequest.delete(conn.connectionId);
220
- }
221
226
  this.connectPool.delete(peerId);
227
+ this.handleConnectionClose(peerId, conn);
222
228
  conn.removeAllListeners();
223
229
  conn.close();
224
230
  (_a = this.peer) === null || _a === void 0 ? void 0 : _a._removeConnection(conn);
@@ -227,16 +233,52 @@ class PeerTransfer {
227
233
  const connections = ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.connections) || {};
228
234
  const conns = connections[peerId] || [];
229
235
  if (conns.length > 0) {
230
- for (const conn of conns) {
231
- conn.removeAllListeners();
232
- conn.close();
233
- (_c = this.peer) === null || _c === void 0 ? void 0 : _c._removeConnection(conn);
236
+ for (const c of conns) {
237
+ c.removeAllListeners();
238
+ c.close();
239
+ (_c = this.peer) === null || _c === void 0 ? void 0 : _c._removeConnection(c);
234
240
  }
235
241
  }
236
242
  }
237
243
  }
244
+ /**
245
+ * 连接关闭时尝试快速重连(5s 超时):
246
+ * - 重连成功 → 保留 inRequest 条目,更新 conn 引用
247
+ * - 重连失败 → 触发 feedback 并清理 inRequest
248
+ */
249
+ handleConnectionClose(peerId, conn) {
250
+ return __awaiter(this, void 0, void 0, function* () {
251
+ const matchedEntries = [];
252
+ for (const [reqId, entry] of this.inRequest) {
253
+ if (entry.conn.connectionId === conn.connectionId) {
254
+ matchedEntries.push([reqId, entry]);
255
+ }
256
+ }
257
+ if (matchedEntries.length === 0) {
258
+ return;
259
+ }
260
+ // 快速重连,超时 5s
261
+ const newConn = yield Promise.race([
262
+ this.connectToPeer(peerId),
263
+ (0, delay_1.default)(5000).then(() => undefined),
264
+ ]);
265
+ if (!this.isValidConnection(newConn)) {
266
+ // 重连失败,触发 feedback 让请求走重试逻辑
267
+ for (const [reqId, entry] of matchedEntries) {
268
+ entry.feedback();
269
+ this.inRequest.delete(reqId);
270
+ }
271
+ return;
272
+ }
273
+ // 重连成功,更新 inRequest 中的 conn 引用
274
+ for (const [reqId, entry] of matchedEntries) {
275
+ if (this.inRequest.has(reqId)) {
276
+ this.inRequest.set(reqId, Object.assign(Object.assign({}, entry), { conn: newConn }));
277
+ }
278
+ }
279
+ });
280
+ }
238
281
  addConnection(conn) {
239
- // console.log('addConnection@', conn.label);
240
282
  this.connectPool.set(conn.peer, conn);
241
283
  const connecting = PeerTransfer.connecting.get(conn.peer);
242
284
  if (connecting) {
@@ -254,21 +296,24 @@ class PeerTransfer {
254
296
  }
255
297
  // turn credential expires
256
298
  if (this.peerConfig.turnExpiredAt && this.peerConfig.turnExpiredAt - 30000 < Date.now()) {
257
- // this.connectPool.clear();
258
299
  yield this.reloadConfig();
259
300
  yield this.createPeer(true);
260
301
  return;
261
302
  }
262
- const deviceIds = this.connectPool.keys();
263
- for (const deviceId of deviceIds) {
303
+ const deviceIds = Array.from(this.connectPool.keys());
304
+ const pingTasks = deviceIds.map((deviceId) => __awaiter(this, void 0, void 0, function* () {
264
305
  const conn = this.connectPool.get(deviceId);
265
306
  if (!this.isValidConnection(conn)) {
266
307
  this.removeConnection(conn.peer, conn.connectionId);
308
+ return;
267
309
  }
268
- else {
269
- this.ping(conn.peer);
310
+ const ok = yield this.ping(conn.peer);
311
+ if (!ok) {
312
+ this.removeConnection(conn.peer, conn.connectionId);
270
313
  }
271
- }
314
+ }));
315
+ // 并行执行,不阻塞下个心跳周期
316
+ yield Promise.allSettled(pingTasks);
272
317
  }), constant_1.HEARTBEAT_INTERVAL);
273
318
  }
274
319
  getApiHost() {
@@ -284,8 +329,7 @@ class PeerTransfer {
284
329
  const apiHost = this.getApiHost();
285
330
  const res = yield axios_1.default.get(`${apiHost}/api/v1/devices/${peerId}/status`);
286
331
  if (res.status < 400) {
287
- const data = res.data;
288
- return data.data;
332
+ return res.data.data;
289
333
  }
290
334
  }
291
335
  catch (err) {
@@ -293,12 +337,11 @@ class PeerTransfer {
293
337
  }
294
338
  });
295
339
  }
296
- connectToPeer(deviceId) {
340
+ connectToPeer(peerId) {
297
341
  return __awaiter(this, void 0, void 0, function* () {
298
- if (deviceId === this.deviceId) {
342
+ if (peerId === this.deviceId) {
299
343
  return;
300
344
  }
301
- const peerId = deviceId;
302
345
  const existingConn = this.connectPool.get(peerId);
303
346
  console.log('connectToPeer', this.isPaused, this.connectPool.size);
304
347
  if (existingConn) {
@@ -321,17 +364,22 @@ class PeerTransfer {
321
364
  }
322
365
  createConnection(peerId) {
323
366
  return __awaiter(this, void 0, void 0, function* () {
324
- let resolve;
325
- let reject;
326
367
  const q = PeerTransfer.connecting.get(peerId);
327
368
  if (q) {
328
369
  return q.promise;
329
370
  }
371
+ let resolve;
372
+ let reject;
330
373
  const connectionPromise = new Promise((res, rej) => {
331
374
  resolve = res;
332
375
  reject = rej;
333
376
  });
334
377
  PeerTransfer.connecting.set(peerId, { promise: connectionPromise, resolve, reject });
378
+ // 兜底超时,防止 connecting 条目永远不清理
379
+ const guardTimer = setTimeout(() => {
380
+ PeerTransfer.connecting.delete(peerId);
381
+ resolve(undefined);
382
+ }, constant_1.CONNECTION_TIMEOUT * 3);
335
383
  const doConnect = () => __awaiter(this, void 0, void 0, function* () {
336
384
  var _a, _b;
337
385
  try {
@@ -344,6 +392,7 @@ class PeerTransfer {
344
392
  }
345
393
  const cur = this.connectPool.get(peerId);
346
394
  if (this.isValidConnection(cur)) {
395
+ clearTimeout(guardTimer);
347
396
  return resolve(cur);
348
397
  }
349
398
  const conn = (_b = this.peer) === null || _b === void 0 ? void 0 : _b.connect(peerId, {
@@ -355,9 +404,11 @@ class PeerTransfer {
355
404
  }
356
405
  const connection = yield this.onConnect(conn).catch(() => { });
357
406
  if (connection) {
407
+ clearTimeout(guardTimer);
358
408
  return resolve(connection);
359
409
  }
360
410
  }
411
+ clearTimeout(guardTimer);
361
412
  resolve(undefined);
362
413
  return undefined;
363
414
  }
@@ -373,6 +424,9 @@ class PeerTransfer {
373
424
  return conn;
374
425
  });
375
426
  }
427
+ /**
428
+ * 心跳检测 - 直接 send,不走 request 管线,避免重试放大
429
+ */
376
430
  ping(peerId) {
377
431
  return __awaiter(this, void 0, void 0, function* () {
378
432
  if (this.deviceId === peerId) {
@@ -380,20 +434,37 @@ class PeerTransfer {
380
434
  }
381
435
  const conn = this.connectPool.get(peerId);
382
436
  if (!this.isValidConnection(conn)) {
383
- this.removeConnection(conn === null || conn === void 0 ? void 0 : conn.peer, conn === null || conn === void 0 ? void 0 : conn.connectionId);
437
+ this.removeConnection(peerId, conn === null || conn === void 0 ? void 0 : conn.connectionId);
384
438
  return;
385
439
  }
386
- const res = yield this.request(conn.peer, {
387
- route: entity_1.PeerRoutes.HEARTBEAT,
388
- method: 'get',
389
- body: {},
390
- timeout: constant_1.PING_TIMEOUT,
391
- });
392
- if (!res || res.error) {
393
- this.removeConnection(conn.peer, conn.connectionId);
440
+ try {
441
+ const requestId = yield this.genRequestId();
442
+ return new Promise((resolve) => {
443
+ const timer = setTimeout(() => {
444
+ this.requests.delete(requestId);
445
+ resolve(false);
446
+ }, constant_1.PING_TIMEOUT);
447
+ this.requests.set(requestId, () => {
448
+ clearTimeout(timer);
449
+ resolve(true);
450
+ });
451
+ conn.send({
452
+ route: entity_1.PeerRoutes.HEARTBEAT,
453
+ method: 'get',
454
+ body: {},
455
+ requestId,
456
+ scope: entity_1.PeerScope.REQUEST,
457
+ action: entity_1.PeerAction.SEND,
458
+ src: this.peerId,
459
+ dest: peerId,
460
+ headers: {},
461
+ createdAt: Date.now(),
462
+ });
463
+ });
464
+ }
465
+ catch (_a) {
394
466
  return false;
395
467
  }
396
- return true;
397
468
  });
398
469
  }
399
470
  onConnect(conn) {
@@ -402,9 +473,9 @@ class PeerTransfer {
402
473
  return;
403
474
  }
404
475
  const exists = this.connectPool.get(conn.peer);
405
- // console.log('onConnect', conn.label, exists);
406
476
  if (exists) {
407
- if (exists.metadata.time > conn.metadata.time) {
477
+ // 双方同时建连时,保留 peerId 字典序大的那条(确定性,不依赖时间戳)
478
+ if (exists.connectionId > conn.connectionId) {
408
479
  this.removeConnection(conn.peer, conn.connectionId);
409
480
  if (this.isValidConnection(exists)) {
410
481
  return exists;
@@ -414,19 +485,24 @@ class PeerTransfer {
414
485
  this.removeConnection(exists.peer, exists.connectionId);
415
486
  }
416
487
  }
417
- let timer = null;
488
+ if (conn.open) {
489
+ this.addConnection(conn);
490
+ this.bindConnectionData(conn);
491
+ return conn;
492
+ }
418
493
  return new Promise((resolve) => {
494
+ let timer = null;
419
495
  const done = (connection) => {
420
- if (connection) {
421
- this.addConnection(connection);
422
- }
423
496
  if (timer) {
424
497
  clearTimeout(timer);
425
498
  timer = null;
426
499
  }
500
+ if (connection) {
501
+ this.addConnection(connection);
502
+ }
427
503
  resolve(connection);
428
504
  };
429
- timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
505
+ timer = setTimeout(() => {
430
506
  if (this.isValidConnection(conn)) {
431
507
  this.addConnection(conn);
432
508
  done(conn);
@@ -435,85 +511,114 @@ class PeerTransfer {
435
511
  this.removeConnection(conn.peer, conn.connectionId);
436
512
  done(undefined);
437
513
  }
438
- timer = null;
439
- }), constant_1.CONNECTION_TIMEOUT);
440
- conn.on('data', (message) => {
441
- switch (message.scope) {
442
- case entity_1.PeerScope.REQUEST:
443
- return this.onRequest(conn, message);
444
- case entity_1.PeerScope.RESPONSE:
445
- return this.onResponse(conn, message);
446
- default:
447
- return this.responseFail(conn, message, 'unknown scope');
448
- }
449
- });
450
- conn.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
514
+ }, constant_1.CONNECTION_TIMEOUT);
515
+ this.bindConnectionData(conn);
516
+ conn.on('error', () => {
451
517
  this.removeConnection(conn.peer, conn.connectionId);
452
- // if (!this.peer?.open) {
453
- // await this.reconnect();
454
- // }
455
518
  done(null);
456
- }));
457
- conn.on('close', () => __awaiter(this, void 0, void 0, function* () {
519
+ });
520
+ conn.on('close', () => {
458
521
  console.log('close------------>', conn.label);
459
- clearTimeout(timer);
460
522
  this.removeConnection(conn.peer, conn.connectionId);
461
- // if (!this.peer?.open) {
462
- // await this.reconnect();
463
- // }
464
523
  done(null);
465
- }));
466
- conn.on('open', () => __awaiter(this, void 0, void 0, function* () {
467
- clearTimeout(timer);
468
- done(conn);
469
- }));
470
- if (this.isValidConnection(conn)) {
471
- if (timer) {
472
- clearTimeout(timer);
473
- }
474
- timer = null;
524
+ });
525
+ conn.on('open', () => {
475
526
  done(conn);
476
- }
527
+ });
477
528
  });
478
529
  });
479
530
  }
531
+ /** 绑定 DataConnection 的 data 事件 */
532
+ bindConnectionData(conn) {
533
+ conn.on('data', (message) => {
534
+ switch (message.scope) {
535
+ case entity_1.PeerScope.REQUEST:
536
+ return this.onRequest(conn, message);
537
+ case entity_1.PeerScope.RESPONSE:
538
+ return this.onResponse(conn, message);
539
+ default:
540
+ return this.responseFail(conn, message, 'unknown scope');
541
+ }
542
+ });
543
+ }
480
544
  request(peerId, payload) {
481
545
  return __awaiter(this, void 0, void 0, function* () {
546
+ var _a;
547
+ const requestId = payload.requestId || (yield this.genRequestId());
548
+ const maxRetry = 2;
549
+ let res;
550
+ for (let attempt = 0; attempt <= maxRetry; attempt++) {
551
+ res = yield this.doRequestOnce(peerId, payload, requestId);
552
+ console.log('request once', res);
553
+ const code = (_a = res.error) === null || _a === void 0 ? void 0 : _a.code;
554
+ if (!res.error || (code !== 'CONN_CLOSED' && code !== 'CONN_FAILED')) {
555
+ return res;
556
+ }
557
+ }
558
+ return res;
559
+ });
560
+ }
561
+ /**
562
+ * 执行一次请求,返回带错误码的结果供外层判断是否重试
563
+ * @param peerId - 目标 peerId
564
+ * @param payload - 请求负载
565
+ * @param requestId - 请求ID(重试时复用)
566
+ * @returns error.code: CONN_CLOSED/CONN_FAILED(可重试)、TIMEOUT(不重试)
567
+ */
568
+ doRequestOnce(peerId, payload, requestId) {
569
+ return __awaiter(this, void 0, void 0, function* () {
570
+ console.log('doRequestOnce@@', peerId, this.destroyed, payload.route);
571
+ if (this.destroyed) {
572
+ return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'destroyed', code: 'CONN_CLOSED' } });
573
+ }
482
574
  const conn = yield this.connectToPeer(peerId);
483
575
  if (!this.isValidConnection(conn)) {
484
- return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'build connection failed', code: -1 } });
576
+ return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'build connection failed', code: 'CONN_FAILED' } });
485
577
  }
486
- const requestId = payload.requestId || (yield this.genRequestId());
487
- const q = new Promise((resolve) => {
488
- const timeout = setTimeout(() => {
489
- const msg = {
490
- body: {},
491
- error: { message: 'request timeout' },
492
- };
578
+ return new Promise((resolve) => {
579
+ let timeout = null;
580
+ const cleanup = () => {
581
+ if (timeout) {
582
+ clearTimeout(timeout);
583
+ timeout = null;
584
+ }
585
+ this.inRequest.delete(requestId);
586
+ this.requests.delete(requestId);
587
+ // 清理 addRequest 可能创建的定时器
588
+ const oldTimer = this.requestTimers.get(requestId);
589
+ if (oldTimer) {
590
+ clearTimeout(oldTimer);
591
+ this.requestTimers.delete(requestId);
592
+ }
593
+ };
594
+ timeout = setTimeout(() => {
595
+ // 先清理,避免 removeConnection 触发 feedback 二次 resolve
596
+ cleanup();
493
597
  this.removeConnection(conn.peer, conn.connectionId);
494
- resolve(msg);
598
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'request timeout', code: 'TIMEOUT' } }));
495
599
  }, payload.timeout || entity_1.REQUEST_TIMEOUT);
496
- this.inRequest.set(conn.connectionId, {
600
+ this.inRequest.set(requestId, {
497
601
  conn,
498
602
  message: payload,
499
- feedback: () => resolve({
500
- body: {},
501
- error: { message: 'send failed' },
502
- }),
603
+ feedback: () => {
604
+ cleanup();
605
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
606
+ },
503
607
  });
504
- this.addRequest(requestId, (message) => {
505
- clearTimeout(timeout);
608
+ // 直接 set,避免 addRequest 内部 setTimeout 在重试时误删新回调
609
+ this.requests.set(requestId, (message) => {
610
+ cleanup();
506
611
  const now = Date.now();
507
612
  if (now - message.createdAt > 3000) {
508
613
  console.warn('[egos] slow peer request:', message.route, now - message.createdAt);
509
614
  }
510
- this.inRequest.delete(conn.connectionId);
511
615
  resolve(message);
512
616
  });
617
+ this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) })).catch(() => {
618
+ cleanup();
619
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
620
+ });
513
621
  });
514
- yield this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) }));
515
- const res = yield q;
516
- return res;
517
622
  });
518
623
  }
519
624
  reply(conn, oldMessage, message) {
@@ -530,7 +635,7 @@ class PeerTransfer {
530
635
  return __awaiter(this, void 0, void 0, function* () {
531
636
  const internalApi = yield this.getApi(conn.peer);
532
637
  let connection = conn;
533
- if (!this.isValidConnection) {
638
+ if (!this.isValidConnection(conn)) {
534
639
  const con = yield this.connectToPeer(conn.peer);
535
640
  if (!con) {
536
641
  return this.responseFail(conn, message, 'send with invalid connection');
@@ -538,8 +643,7 @@ class PeerTransfer {
538
643
  connection = con;
539
644
  }
540
645
  const requestId = message.requestId || (yield this.genRequestId());
541
- const sent = yield connection.send(Object.assign(Object.assign({}, message), { requestId, headers: Object.assign(Object.assign({}, message.headers), { ['x-device-token']: internalApi === null || internalApi === void 0 ? void 0 : internalApi.token }), scope: entity_1.PeerScope.REQUEST, action: entity_1.PeerAction.SEND, src: this.peerId, dest: conn.peer, createdAt: Date.now() }), false);
542
- return sent;
646
+ return connection.send(Object.assign(Object.assign({}, message), { requestId, headers: Object.assign(Object.assign({}, message.headers), { ['x-device-token']: internalApi === null || internalApi === void 0 ? void 0 : internalApi.token }), scope: entity_1.PeerScope.REQUEST, action: entity_1.PeerAction.SEND, src: this.peerId, dest: conn.peer, createdAt: Date.now() }), false);
543
647
  });
544
648
  }
545
649
  inheritMessage(receiveMsg) {
@@ -554,6 +658,7 @@ class PeerTransfer {
554
658
  }
555
659
  reconnect() {
556
660
  return __awaiter(this, void 0, void 0, function* () {
661
+ console.log('reconnect', this.isPaused);
557
662
  if (this.isPaused) {
558
663
  return;
559
664
  }
@@ -582,20 +687,21 @@ class PeerTransfer {
582
687
  if (this.isPaused) {
583
688
  return;
584
689
  }
690
+ // 已连接且 token 未变,无需重连
585
691
  if (((_a = this.peer) === null || _a === void 0 ? void 0 : _a.open) && this.peerConfig.token === ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.options.token)) {
586
692
  return this.peer;
587
693
  }
694
+ // 指数退避
695
+ this.reconnectAttempt++;
696
+ const backoff = Math.min(BASE_RECONNECT_BACKOFF * Math.pow(2, this.reconnectAttempt - 1), MAX_RECONNECT_BACKOFF);
697
+ yield (0, delay_1.default)(backoff);
588
698
  yield this.reloadConfig();
589
- return this.createPeer();
590
- // return this.createPeer();
699
+ // 优先尝试 peer.reconnect() 轻量重连
591
700
  if (((_c = this.peer) === null || _c === void 0 ? void 0 : _c.disconnected) && ((_d = this.peer) === null || _d === void 0 ? void 0 : _d.options.token) === this.peerConfig.token) {
592
701
  this.peer.reconnect();
593
702
  return this.peer;
594
703
  }
595
- else {
596
- yield this.reloadConfig();
597
- return this.createPeer();
598
- }
704
+ return this.createPeer();
599
705
  }
600
706
  catch (err) {
601
707
  return this.createPeer();
@@ -604,11 +710,23 @@ class PeerTransfer {
604
710
  }
605
711
  clearInstance() {
606
712
  var _a, _b;
713
+ console.info('clearInstance');
714
+ this.destroyed = true;
607
715
  PeerTransfer.connecting.clear();
608
- const peerIds = this.connectPool.keys();
716
+ // 先触发所有 inRequest 的 feedback,让它们 resolve
717
+ for (const [reqId, entry] of this.inRequest) {
718
+ entry.feedback();
719
+ }
720
+ this.inRequest.clear();
721
+ const peerIds = Array.from(this.connectPool.keys());
609
722
  for (const pId of peerIds) {
610
723
  this.removeConnection(pId);
611
724
  }
725
+ this.requests.clear();
726
+ for (const timer of this.requestTimers.values()) {
727
+ clearTimeout(timer);
728
+ }
729
+ this.requestTimers.clear();
612
730
  (_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
613
731
  (_b = this.peer) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
614
732
  this.booted = undefined;
@@ -625,9 +743,7 @@ class PeerTransfer {
625
743
  onRequest(conn, message) {
626
744
  return __awaiter(this, void 0, void 0, function* () {
627
745
  try {
628
- // @todo default folder
629
- // console.log('onRequest-->', message.route);
630
- // @todo default folder
746
+ console.log('onRequest@@', conn.peer, message.route);
631
747
  yield this.router.run(conn, message);
632
748
  }
633
749
  catch (err) {
@@ -637,37 +753,35 @@ class PeerTransfer {
637
753
  }
638
754
  onResponse(conn, message) {
639
755
  return __awaiter(this, void 0, void 0, function* () {
640
- // @todo default folder
641
756
  const cb = this.requests.get(message.requestId);
642
757
  if (cb) {
643
758
  cb.apply(this, [message]);
644
759
  this.requests.delete(message.requestId);
645
760
  }
646
- // @todo throw err;
647
761
  });
648
762
  }
649
763
  responseOk(conn, message) {
650
764
  return __awaiter(this, void 0, void 0, function* () {
651
- const msg = {
652
- body: { ok: true },
653
- };
654
- return this.reply(conn, message, msg);
765
+ return this.reply(conn, message, { body: { ok: true } });
655
766
  });
656
767
  }
657
768
  responseFail(conn, message, err) {
658
769
  return __awaiter(this, void 0, void 0, function* () {
659
- const msg = {
660
- body: {},
661
- error: err || 'service error',
662
- };
663
- return this.reply(conn, message, msg);
770
+ return this.reply(conn, message, { body: {}, error: err || 'service error' });
664
771
  });
665
772
  }
666
773
  addRequest(id, cb) {
774
+ // 清除已有定时器,避免重复注册时旧定时器误删新回调
775
+ const oldTimer = this.requestTimers.get(id);
776
+ if (oldTimer) {
777
+ clearTimeout(oldTimer);
778
+ }
667
779
  this.requests.set(id, cb);
668
- setTimeout(() => {
780
+ const timer = setTimeout(() => {
669
781
  this.requests.delete(id);
782
+ this.requestTimers.delete(id);
670
783
  }, this.timeout);
784
+ this.requestTimers.set(id, timer);
671
785
  }
672
786
  use(handler, prefix = '') {
673
787
  this.router.use(handler, prefix);
@@ -681,6 +795,7 @@ class PeerTransfer {
681
795
  }
682
796
  resume() {
683
797
  this.isPaused = false;
798
+ this.destroyed = false;
684
799
  return this.createPeer(true);
685
800
  }
686
801
  }