egos-transfer 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,81 +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.onConnectLimit();
112
- return done(null);
113
- }
114
- if (err.message == constant_1.UNAUTHORIZED) {
115
- done(null);
116
- return this.requireAuth();
117
- }
118
- if (err.type === 'network') {
119
- done(null);
120
- return this.reconnect();
121
- }
122
- if (err.type === 'server-error') {
123
- done(null);
124
- return this.reconnect();
125
- }
126
- done(null);
127
- return this.createPeer(true);
128
- }));
129
- });
130
- peer.on('connection', (conn) => {
131
- conn.label = 'incoming';
132
- 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.once('open', (id) => {
100
+ console.log('peer@open');
101
+ this.reconnectAttempt = 0; // 连接成功,重置退避计数
102
+ done(peer);
133
103
  });
134
- const socket = peer.socket;
135
- socket === null || socket === void 0 ? void 0 : socket.on('message', (message) => {
136
- 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);
137
115
  });
138
- 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 {
139
158
  yield this.booted;
140
159
  }
141
- catch (err) {
142
- this.booted = undefined;
160
+ catch (_a) {
161
+ // booted rejected via timer timeout
143
162
  }
144
163
  finally {
145
164
  this.booted = undefined;
@@ -157,10 +176,7 @@ class PeerTransfer {
157
176
  this.onHeartbeat(message);
158
177
  }
159
178
  onHeartbeat(message) {
160
- if (message.type !== 'HEARTBEAT') {
161
- return;
162
- }
163
- if (this.isPaused) {
179
+ if (message.type !== 'HEARTBEAT' || this.isPaused) {
164
180
  return;
165
181
  }
166
182
  }
@@ -179,7 +195,6 @@ class PeerTransfer {
179
195
  message: 'peer conflict',
180
196
  },
181
197
  });
182
- return;
183
198
  }
184
199
  }
185
200
  onOpen(message) {
@@ -192,32 +207,24 @@ class PeerTransfer {
192
207
  if (message.type !== 'ONLINE') {
193
208
  return;
194
209
  }
195
- const deviceId = message.payload.deviceId;
196
- this.online(deviceId);
210
+ this.online(message.payload.deviceId);
197
211
  });
198
212
  }
199
213
  onDeviceOffline(message) {
200
214
  if (message.type !== 'OFFLINE') {
201
215
  return;
202
216
  }
203
- const deviceId = message.payload.deviceId;
204
- this.ping(deviceId);
217
+ this.ping(message.payload.deviceId);
205
218
  }
206
219
  removeConnection(peerId, connectionId) {
207
220
  var _a, _b, _c;
208
- // console.trace('removeConnection@', peerId, connectionId);
209
221
  const conn = this.connectPool.get(peerId);
210
222
  if (conn) {
211
223
  if (connectionId && conn.connectionId !== connectionId) {
212
224
  return;
213
225
  }
214
- const sending = this.inRequest.get(conn.connectionId);
215
- if (sending) {
216
- // 提前结束request,避免关闭连接时触发反馈
217
- sending.feedback();
218
- this.inRequest.delete(conn.connectionId);
219
- }
220
226
  this.connectPool.delete(peerId);
227
+ this.handleConnectionClose(peerId, conn);
221
228
  conn.removeAllListeners();
222
229
  conn.close();
223
230
  (_a = this.peer) === null || _a === void 0 ? void 0 : _a._removeConnection(conn);
@@ -226,16 +233,52 @@ class PeerTransfer {
226
233
  const connections = ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.connections) || {};
227
234
  const conns = connections[peerId] || [];
228
235
  if (conns.length > 0) {
229
- for (const conn of conns) {
230
- conn.removeAllListeners();
231
- conn.close();
232
- (_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);
233
240
  }
234
241
  }
235
242
  }
236
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
+ }
237
281
  addConnection(conn) {
238
- // console.log('addConnection@', conn.label);
239
282
  this.connectPool.set(conn.peer, conn);
240
283
  const connecting = PeerTransfer.connecting.get(conn.peer);
241
284
  if (connecting) {
@@ -253,21 +296,24 @@ class PeerTransfer {
253
296
  }
254
297
  // turn credential expires
255
298
  if (this.peerConfig.turnExpiredAt && this.peerConfig.turnExpiredAt - 30000 < Date.now()) {
256
- // this.connectPool.clear();
257
299
  yield this.reloadConfig();
258
300
  yield this.createPeer(true);
259
301
  return;
260
302
  }
261
- const deviceIds = this.connectPool.keys();
262
- 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* () {
263
305
  const conn = this.connectPool.get(deviceId);
264
306
  if (!this.isValidConnection(conn)) {
265
307
  this.removeConnection(conn.peer, conn.connectionId);
308
+ return;
266
309
  }
267
- else {
268
- this.ping(conn.peer);
310
+ const ok = yield this.ping(conn.peer);
311
+ if (!ok) {
312
+ this.removeConnection(conn.peer, conn.connectionId);
269
313
  }
270
- }
314
+ }));
315
+ // 并行执行,不阻塞下个心跳周期
316
+ yield Promise.allSettled(pingTasks);
271
317
  }), constant_1.HEARTBEAT_INTERVAL);
272
318
  }
273
319
  getApiHost() {
@@ -283,8 +329,7 @@ class PeerTransfer {
283
329
  const apiHost = this.getApiHost();
284
330
  const res = yield axios_1.default.get(`${apiHost}/api/v1/devices/${peerId}/status`);
285
331
  if (res.status < 400) {
286
- const data = res.data;
287
- return data.data;
332
+ return res.data.data;
288
333
  }
289
334
  }
290
335
  catch (err) {
@@ -292,12 +337,11 @@ class PeerTransfer {
292
337
  }
293
338
  });
294
339
  }
295
- connectToPeer(deviceId) {
340
+ connectToPeer(peerId) {
296
341
  return __awaiter(this, void 0, void 0, function* () {
297
- if (deviceId === this.deviceId) {
342
+ if (peerId === this.deviceId) {
298
343
  return;
299
344
  }
300
- const peerId = deviceId;
301
345
  const existingConn = this.connectPool.get(peerId);
302
346
  console.log('connectToPeer', this.isPaused, this.connectPool.size);
303
347
  if (existingConn) {
@@ -320,17 +364,22 @@ class PeerTransfer {
320
364
  }
321
365
  createConnection(peerId) {
322
366
  return __awaiter(this, void 0, void 0, function* () {
323
- let resolve;
324
- let reject;
325
367
  const q = PeerTransfer.connecting.get(peerId);
326
368
  if (q) {
327
369
  return q.promise;
328
370
  }
371
+ let resolve;
372
+ let reject;
329
373
  const connectionPromise = new Promise((res, rej) => {
330
374
  resolve = res;
331
375
  reject = rej;
332
376
  });
333
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);
334
383
  const doConnect = () => __awaiter(this, void 0, void 0, function* () {
335
384
  var _a, _b;
336
385
  try {
@@ -341,8 +390,10 @@ class PeerTransfer {
341
390
  if (!((_a = this.peer) === null || _a === void 0 ? void 0 : _a.open)) {
342
391
  yield this.reconnect();
343
392
  }
393
+ // 重试前检查:对方可能已经建连成功(duplicate 场景)
344
394
  const cur = this.connectPool.get(peerId);
345
395
  if (this.isValidConnection(cur)) {
396
+ clearTimeout(guardTimer);
346
397
  return resolve(cur);
347
398
  }
348
399
  const conn = (_b = this.peer) === null || _b === void 0 ? void 0 : _b.connect(peerId, {
@@ -354,9 +405,17 @@ class PeerTransfer {
354
405
  }
355
406
  const connection = yield this.onConnect(conn).catch(() => { });
356
407
  if (connection) {
408
+ clearTimeout(guardTimer);
357
409
  return resolve(connection);
358
410
  }
411
+ // duplicate 场景:onConnect 返回 undefined 但连接池可能已有对方建的连接
412
+ const pooled = this.connectPool.get(peerId);
413
+ if (this.isValidConnection(pooled)) {
414
+ clearTimeout(guardTimer);
415
+ return resolve(pooled);
416
+ }
359
417
  }
418
+ clearTimeout(guardTimer);
360
419
  resolve(undefined);
361
420
  return undefined;
362
421
  }
@@ -372,6 +431,9 @@ class PeerTransfer {
372
431
  return conn;
373
432
  });
374
433
  }
434
+ /**
435
+ * 心跳检测 - 直接 send,不走 request 管线,避免重试放大
436
+ */
375
437
  ping(peerId) {
376
438
  return __awaiter(this, void 0, void 0, function* () {
377
439
  if (this.deviceId === peerId) {
@@ -379,20 +441,37 @@ class PeerTransfer {
379
441
  }
380
442
  const conn = this.connectPool.get(peerId);
381
443
  if (!this.isValidConnection(conn)) {
382
- this.removeConnection(conn === null || conn === void 0 ? void 0 : conn.peer, conn === null || conn === void 0 ? void 0 : conn.connectionId);
444
+ this.removeConnection(peerId, conn === null || conn === void 0 ? void 0 : conn.connectionId);
383
445
  return;
384
446
  }
385
- const res = yield this.request(conn.peer, {
386
- route: entity_1.PeerRoutes.HEARTBEAT,
387
- method: 'get',
388
- body: {},
389
- timeout: constant_1.PING_TIMEOUT,
390
- });
391
- if (!res || res.error) {
392
- this.removeConnection(conn.peer, conn.connectionId);
447
+ try {
448
+ const requestId = yield this.genRequestId();
449
+ return new Promise((resolve) => {
450
+ const timer = setTimeout(() => {
451
+ this.requests.delete(requestId);
452
+ resolve(false);
453
+ }, constant_1.PING_TIMEOUT);
454
+ this.requests.set(requestId, () => {
455
+ clearTimeout(timer);
456
+ resolve(true);
457
+ });
458
+ conn.send({
459
+ route: entity_1.PeerRoutes.HEARTBEAT,
460
+ method: 'get',
461
+ body: {},
462
+ requestId,
463
+ scope: entity_1.PeerScope.REQUEST,
464
+ action: entity_1.PeerAction.SEND,
465
+ src: this.peerId,
466
+ dest: peerId,
467
+ headers: {},
468
+ createdAt: Date.now(),
469
+ });
470
+ });
471
+ }
472
+ catch (_a) {
393
473
  return false;
394
474
  }
395
- return true;
396
475
  });
397
476
  }
398
477
  onConnect(conn) {
@@ -401,9 +480,9 @@ class PeerTransfer {
401
480
  return;
402
481
  }
403
482
  const exists = this.connectPool.get(conn.peer);
404
- // console.log('onConnect', conn.label, exists);
405
483
  if (exists) {
406
- if (exists.metadata.time > conn.metadata.time) {
484
+ // 双方同时建连时,保留 peerId 字典序大的那条(确定性,不依赖时间戳)
485
+ if (exists.connectionId > conn.connectionId) {
407
486
  this.removeConnection(conn.peer, conn.connectionId);
408
487
  if (this.isValidConnection(exists)) {
409
488
  return exists;
@@ -413,19 +492,24 @@ class PeerTransfer {
413
492
  this.removeConnection(exists.peer, exists.connectionId);
414
493
  }
415
494
  }
416
- let timer = null;
495
+ if (conn.open) {
496
+ this.addConnection(conn);
497
+ this.bindConnectionData(conn);
498
+ return conn;
499
+ }
417
500
  return new Promise((resolve) => {
501
+ let timer = null;
418
502
  const done = (connection) => {
419
- if (connection) {
420
- this.addConnection(connection);
421
- }
422
503
  if (timer) {
423
504
  clearTimeout(timer);
424
505
  timer = null;
425
506
  }
507
+ if (connection) {
508
+ this.addConnection(connection);
509
+ }
426
510
  resolve(connection);
427
511
  };
428
- timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
512
+ timer = setTimeout(() => {
429
513
  if (this.isValidConnection(conn)) {
430
514
  this.addConnection(conn);
431
515
  done(conn);
@@ -434,85 +518,120 @@ class PeerTransfer {
434
518
  this.removeConnection(conn.peer, conn.connectionId);
435
519
  done(undefined);
436
520
  }
437
- timer = null;
438
- }), constant_1.CONNECTION_TIMEOUT);
439
- conn.on('data', (message) => {
440
- switch (message.scope) {
441
- case entity_1.PeerScope.REQUEST:
442
- return this.onRequest(conn, message);
443
- case entity_1.PeerScope.RESPONSE:
444
- return this.onResponse(conn, message);
445
- default:
446
- return this.responseFail(conn, message, 'unknown scope');
521
+ }, constant_1.CONNECTION_TIMEOUT);
522
+ this.bindConnectionData(conn);
523
+ conn.on('error', (err) => {
524
+ // duplicate 场景:连接池可能已有对方建的连接,不应删除
525
+ const pooled = this.connectPool.get(conn.peer);
526
+ if (this.isValidConnection(pooled)) {
527
+ done(pooled);
528
+ return;
447
529
  }
448
- });
449
- conn.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
450
530
  this.removeConnection(conn.peer, conn.connectionId);
451
- // if (!this.peer?.open) {
452
- // await this.reconnect();
453
- // }
454
531
  done(null);
455
- }));
456
- conn.on('close', () => __awaiter(this, void 0, void 0, function* () {
532
+ });
533
+ conn.on('close', () => {
457
534
  console.log('close------------>', conn.label);
458
- clearTimeout(timer);
459
535
  this.removeConnection(conn.peer, conn.connectionId);
460
- // if (!this.peer?.open) {
461
- // await this.reconnect();
462
- // }
463
536
  done(null);
464
- }));
465
- conn.on('open', () => __awaiter(this, void 0, void 0, function* () {
466
- clearTimeout(timer);
467
- done(conn);
468
- }));
469
- if (this.isValidConnection(conn)) {
470
- if (timer) {
471
- clearTimeout(timer);
472
- }
473
- timer = null;
537
+ });
538
+ conn.on('open', () => {
474
539
  done(conn);
475
- }
540
+ });
476
541
  });
477
542
  });
478
543
  }
544
+ /** 绑定 DataConnection 的 data 事件 */
545
+ bindConnectionData(conn) {
546
+ conn.on('data', (message) => {
547
+ switch (message.scope) {
548
+ case entity_1.PeerScope.REQUEST:
549
+ return this.onRequest(conn, message);
550
+ case entity_1.PeerScope.RESPONSE:
551
+ return this.onResponse(conn, message);
552
+ default:
553
+ return this.responseFail(conn, message, 'unknown scope');
554
+ }
555
+ });
556
+ }
479
557
  request(peerId, payload) {
480
558
  return __awaiter(this, void 0, void 0, function* () {
559
+ var _a;
560
+ const requestId = payload.requestId || (yield this.genRequestId());
561
+ const maxRetry = 2;
562
+ let res;
563
+ for (let attempt = 0; attempt <= maxRetry; attempt++) {
564
+ res = yield this.doRequestOnce(peerId, payload, requestId);
565
+ console.log('request once', res);
566
+ const code = (_a = res.error) === null || _a === void 0 ? void 0 : _a.code;
567
+ if (!res.error || (code !== 'CONN_CLOSED' && code !== 'CONN_FAILED')) {
568
+ return res;
569
+ }
570
+ }
571
+ return res;
572
+ });
573
+ }
574
+ /**
575
+ * 执行一次请求,返回带错误码的结果供外层判断是否重试
576
+ * @param peerId - 目标 peerId
577
+ * @param payload - 请求负载
578
+ * @param requestId - 请求ID(重试时复用)
579
+ * @returns error.code: CONN_CLOSED/CONN_FAILED(可重试)、TIMEOUT(不重试)
580
+ */
581
+ doRequestOnce(peerId, payload, requestId) {
582
+ return __awaiter(this, void 0, void 0, function* () {
583
+ console.log('doRequestOnce@@', peerId, this.destroyed, payload.route);
584
+ if (this.destroyed) {
585
+ return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'destroyed', code: 'CONN_CLOSED' } });
586
+ }
481
587
  const conn = yield this.connectToPeer(peerId);
482
588
  if (!this.isValidConnection(conn)) {
483
- return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'build connection failed', code: -1 } });
589
+ return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'build connection failed', code: 'CONN_FAILED' } });
484
590
  }
485
- const requestId = payload.requestId || (yield this.genRequestId());
486
- const q = new Promise((resolve) => {
487
- const timeout = setTimeout(() => {
488
- const msg = {
489
- body: {},
490
- error: { message: 'request timeout' },
491
- };
591
+ return new Promise((resolve) => {
592
+ let timeout = null;
593
+ const cleanup = () => {
594
+ if (timeout) {
595
+ clearTimeout(timeout);
596
+ timeout = null;
597
+ }
598
+ this.inRequest.delete(requestId);
599
+ this.requests.delete(requestId);
600
+ // 清理 addRequest 可能创建的定时器
601
+ const oldTimer = this.requestTimers.get(requestId);
602
+ if (oldTimer) {
603
+ clearTimeout(oldTimer);
604
+ this.requestTimers.delete(requestId);
605
+ }
606
+ };
607
+ timeout = setTimeout(() => {
608
+ // 先清理,避免 removeConnection 触发 feedback 二次 resolve
609
+ cleanup();
492
610
  this.removeConnection(conn.peer, conn.connectionId);
493
- resolve(msg);
611
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'request timeout', code: 'TIMEOUT' } }));
494
612
  }, payload.timeout || entity_1.REQUEST_TIMEOUT);
495
- this.inRequest.set(conn.connectionId, {
613
+ this.inRequest.set(requestId, {
496
614
  conn,
497
615
  message: payload,
498
- feedback: () => resolve({
499
- body: {},
500
- error: { message: 'send failed' },
501
- }),
616
+ feedback: () => {
617
+ cleanup();
618
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
619
+ },
502
620
  });
503
- this.addRequest(requestId, (message) => {
504
- clearTimeout(timeout);
621
+ // 直接 set,避免 addRequest 内部 setTimeout 在重试时误删新回调
622
+ this.requests.set(requestId, (message) => {
623
+ cleanup();
505
624
  const now = Date.now();
506
625
  if (now - message.createdAt > 3000) {
507
626
  console.warn('[egos] slow peer request:', message.route, now - message.createdAt);
508
627
  }
509
- this.inRequest.delete(conn.connectionId);
510
628
  resolve(message);
511
629
  });
630
+ this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) })).catch(() => {
631
+ cleanup();
632
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
633
+ });
512
634
  });
513
- yield this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) }));
514
- const res = yield q;
515
- return res;
516
635
  });
517
636
  }
518
637
  reply(conn, oldMessage, message) {
@@ -529,7 +648,7 @@ class PeerTransfer {
529
648
  return __awaiter(this, void 0, void 0, function* () {
530
649
  const internalApi = yield this.getApi(conn.peer);
531
650
  let connection = conn;
532
- if (!this.isValidConnection) {
651
+ if (!this.isValidConnection(conn)) {
533
652
  const con = yield this.connectToPeer(conn.peer);
534
653
  if (!con) {
535
654
  return this.responseFail(conn, message, 'send with invalid connection');
@@ -537,8 +656,7 @@ class PeerTransfer {
537
656
  connection = con;
538
657
  }
539
658
  const requestId = message.requestId || (yield this.genRequestId());
540
- 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);
541
- return sent;
659
+ 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);
542
660
  });
543
661
  }
544
662
  inheritMessage(receiveMsg) {
@@ -553,6 +671,7 @@ class PeerTransfer {
553
671
  }
554
672
  reconnect() {
555
673
  return __awaiter(this, void 0, void 0, function* () {
674
+ console.log('reconnect', this.isPaused);
556
675
  if (this.isPaused) {
557
676
  return;
558
677
  }
@@ -581,20 +700,21 @@ class PeerTransfer {
581
700
  if (this.isPaused) {
582
701
  return;
583
702
  }
703
+ // 已连接且 token 未变,无需重连
584
704
  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)) {
585
705
  return this.peer;
586
706
  }
707
+ // 指数退避
708
+ this.reconnectAttempt++;
709
+ const backoff = Math.min(BASE_RECONNECT_BACKOFF * Math.pow(2, this.reconnectAttempt - 1), MAX_RECONNECT_BACKOFF);
710
+ yield (0, delay_1.default)(backoff);
587
711
  yield this.reloadConfig();
588
- return this.createPeer();
589
- // return this.createPeer();
712
+ // 优先尝试 peer.reconnect() 轻量重连
590
713
  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) {
591
714
  this.peer.reconnect();
592
715
  return this.peer;
593
716
  }
594
- else {
595
- yield this.reloadConfig();
596
- return this.createPeer();
597
- }
717
+ return this.createPeer();
598
718
  }
599
719
  catch (err) {
600
720
  return this.createPeer();
@@ -603,11 +723,23 @@ class PeerTransfer {
603
723
  }
604
724
  clearInstance() {
605
725
  var _a, _b;
726
+ console.info('clearInstance');
727
+ this.destroyed = true;
606
728
  PeerTransfer.connecting.clear();
607
- const peerIds = this.connectPool.keys();
729
+ // 先触发所有 inRequest 的 feedback,让它们 resolve
730
+ for (const [reqId, entry] of this.inRequest) {
731
+ entry.feedback();
732
+ }
733
+ this.inRequest.clear();
734
+ const peerIds = Array.from(this.connectPool.keys());
608
735
  for (const pId of peerIds) {
609
736
  this.removeConnection(pId);
610
737
  }
738
+ this.requests.clear();
739
+ for (const timer of this.requestTimers.values()) {
740
+ clearTimeout(timer);
741
+ }
742
+ this.requestTimers.clear();
611
743
  (_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
612
744
  (_b = this.peer) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
613
745
  this.booted = undefined;
@@ -623,10 +755,9 @@ class PeerTransfer {
623
755
  }
624
756
  onRequest(conn, message) {
625
757
  return __awaiter(this, void 0, void 0, function* () {
758
+ var _a;
626
759
  try {
627
- // @todo default folder
628
- // console.log('onRequest-->', message.route);
629
- // @todo default folder
760
+ console.log('onRequest@@', (_a = conn.peer) === null || _a === void 0 ? void 0 : _a.substring(0, 4), message.route);
630
761
  yield this.router.run(conn, message);
631
762
  }
632
763
  catch (err) {
@@ -636,37 +767,35 @@ class PeerTransfer {
636
767
  }
637
768
  onResponse(conn, message) {
638
769
  return __awaiter(this, void 0, void 0, function* () {
639
- // @todo default folder
640
770
  const cb = this.requests.get(message.requestId);
641
771
  if (cb) {
642
772
  cb.apply(this, [message]);
643
773
  this.requests.delete(message.requestId);
644
774
  }
645
- // @todo throw err;
646
775
  });
647
776
  }
648
777
  responseOk(conn, message) {
649
778
  return __awaiter(this, void 0, void 0, function* () {
650
- const msg = {
651
- body: { ok: true },
652
- };
653
- return this.reply(conn, message, msg);
779
+ return this.reply(conn, message, { body: { ok: true } });
654
780
  });
655
781
  }
656
782
  responseFail(conn, message, err) {
657
783
  return __awaiter(this, void 0, void 0, function* () {
658
- const msg = {
659
- body: {},
660
- error: err || 'service error',
661
- };
662
- return this.reply(conn, message, msg);
784
+ return this.reply(conn, message, { body: {}, error: err || 'service error' });
663
785
  });
664
786
  }
665
787
  addRequest(id, cb) {
788
+ // 清除已有定时器,避免重复注册时旧定时器误删新回调
789
+ const oldTimer = this.requestTimers.get(id);
790
+ if (oldTimer) {
791
+ clearTimeout(oldTimer);
792
+ }
666
793
  this.requests.set(id, cb);
667
- setTimeout(() => {
794
+ const timer = setTimeout(() => {
668
795
  this.requests.delete(id);
796
+ this.requestTimers.delete(id);
669
797
  }, this.timeout);
798
+ this.requestTimers.set(id, timer);
670
799
  }
671
800
  use(handler, prefix = '') {
672
801
  this.router.use(handler, prefix);
@@ -680,6 +809,7 @@ class PeerTransfer {
680
809
  }
681
810
  resume() {
682
811
  this.isPaused = false;
812
+ this.destroyed = false;
683
813
  return this.createPeer(true);
684
814
  }
685
815
  }