egos-transfer 0.2.3 → 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,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.on('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 {
@@ -343,6 +392,7 @@ class PeerTransfer {
343
392
  }
344
393
  const cur = this.connectPool.get(peerId);
345
394
  if (this.isValidConnection(cur)) {
395
+ clearTimeout(guardTimer);
346
396
  return resolve(cur);
347
397
  }
348
398
  const conn = (_b = this.peer) === null || _b === void 0 ? void 0 : _b.connect(peerId, {
@@ -354,9 +404,11 @@ class PeerTransfer {
354
404
  }
355
405
  const connection = yield this.onConnect(conn).catch(() => { });
356
406
  if (connection) {
407
+ clearTimeout(guardTimer);
357
408
  return resolve(connection);
358
409
  }
359
410
  }
411
+ clearTimeout(guardTimer);
360
412
  resolve(undefined);
361
413
  return undefined;
362
414
  }
@@ -372,6 +424,9 @@ class PeerTransfer {
372
424
  return conn;
373
425
  });
374
426
  }
427
+ /**
428
+ * 心跳检测 - 直接 send,不走 request 管线,避免重试放大
429
+ */
375
430
  ping(peerId) {
376
431
  return __awaiter(this, void 0, void 0, function* () {
377
432
  if (this.deviceId === peerId) {
@@ -379,20 +434,37 @@ class PeerTransfer {
379
434
  }
380
435
  const conn = this.connectPool.get(peerId);
381
436
  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);
437
+ this.removeConnection(peerId, conn === null || conn === void 0 ? void 0 : conn.connectionId);
383
438
  return;
384
439
  }
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);
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) {
393
466
  return false;
394
467
  }
395
- return true;
396
468
  });
397
469
  }
398
470
  onConnect(conn) {
@@ -401,9 +473,9 @@ class PeerTransfer {
401
473
  return;
402
474
  }
403
475
  const exists = this.connectPool.get(conn.peer);
404
- // console.log('onConnect', conn.label, exists);
405
476
  if (exists) {
406
- if (exists.metadata.time > conn.metadata.time) {
477
+ // 双方同时建连时,保留 peerId 字典序大的那条(确定性,不依赖时间戳)
478
+ if (exists.connectionId > conn.connectionId) {
407
479
  this.removeConnection(conn.peer, conn.connectionId);
408
480
  if (this.isValidConnection(exists)) {
409
481
  return exists;
@@ -413,19 +485,24 @@ class PeerTransfer {
413
485
  this.removeConnection(exists.peer, exists.connectionId);
414
486
  }
415
487
  }
416
- let timer = null;
488
+ if (conn.open) {
489
+ this.addConnection(conn);
490
+ this.bindConnectionData(conn);
491
+ return conn;
492
+ }
417
493
  return new Promise((resolve) => {
494
+ let timer = null;
418
495
  const done = (connection) => {
419
- if (connection) {
420
- this.addConnection(connection);
421
- }
422
496
  if (timer) {
423
497
  clearTimeout(timer);
424
498
  timer = null;
425
499
  }
500
+ if (connection) {
501
+ this.addConnection(connection);
502
+ }
426
503
  resolve(connection);
427
504
  };
428
- timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
505
+ timer = setTimeout(() => {
429
506
  if (this.isValidConnection(conn)) {
430
507
  this.addConnection(conn);
431
508
  done(conn);
@@ -434,85 +511,114 @@ class PeerTransfer {
434
511
  this.removeConnection(conn.peer, conn.connectionId);
435
512
  done(undefined);
436
513
  }
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');
447
- }
448
- });
449
- conn.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
514
+ }, constant_1.CONNECTION_TIMEOUT);
515
+ this.bindConnectionData(conn);
516
+ conn.on('error', () => {
450
517
  this.removeConnection(conn.peer, conn.connectionId);
451
- // if (!this.peer?.open) {
452
- // await this.reconnect();
453
- // }
454
518
  done(null);
455
- }));
456
- conn.on('close', () => __awaiter(this, void 0, void 0, function* () {
519
+ });
520
+ conn.on('close', () => {
457
521
  console.log('close------------>', conn.label);
458
- clearTimeout(timer);
459
522
  this.removeConnection(conn.peer, conn.connectionId);
460
- // if (!this.peer?.open) {
461
- // await this.reconnect();
462
- // }
463
523
  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;
524
+ });
525
+ conn.on('open', () => {
474
526
  done(conn);
475
- }
527
+ });
476
528
  });
477
529
  });
478
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
+ }
479
544
  request(peerId, payload) {
480
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
+ }
481
574
  const conn = yield this.connectToPeer(peerId);
482
575
  if (!this.isValidConnection(conn)) {
483
- 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' } });
484
577
  }
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
- };
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();
492
597
  this.removeConnection(conn.peer, conn.connectionId);
493
- resolve(msg);
598
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'request timeout', code: 'TIMEOUT' } }));
494
599
  }, payload.timeout || entity_1.REQUEST_TIMEOUT);
495
- this.inRequest.set(conn.connectionId, {
600
+ this.inRequest.set(requestId, {
496
601
  conn,
497
602
  message: payload,
498
- feedback: () => resolve({
499
- body: {},
500
- error: { message: 'send failed' },
501
- }),
603
+ feedback: () => {
604
+ cleanup();
605
+ resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
606
+ },
502
607
  });
503
- this.addRequest(requestId, (message) => {
504
- clearTimeout(timeout);
608
+ // 直接 set,避免 addRequest 内部 setTimeout 在重试时误删新回调
609
+ this.requests.set(requestId, (message) => {
610
+ cleanup();
505
611
  const now = Date.now();
506
612
  if (now - message.createdAt > 3000) {
507
613
  console.warn('[egos] slow peer request:', message.route, now - message.createdAt);
508
614
  }
509
- this.inRequest.delete(conn.connectionId);
510
615
  resolve(message);
511
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
+ });
512
621
  });
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
622
  });
517
623
  }
518
624
  reply(conn, oldMessage, message) {
@@ -529,7 +635,7 @@ class PeerTransfer {
529
635
  return __awaiter(this, void 0, void 0, function* () {
530
636
  const internalApi = yield this.getApi(conn.peer);
531
637
  let connection = conn;
532
- if (!this.isValidConnection) {
638
+ if (!this.isValidConnection(conn)) {
533
639
  const con = yield this.connectToPeer(conn.peer);
534
640
  if (!con) {
535
641
  return this.responseFail(conn, message, 'send with invalid connection');
@@ -537,8 +643,7 @@ class PeerTransfer {
537
643
  connection = con;
538
644
  }
539
645
  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;
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);
542
647
  });
543
648
  }
544
649
  inheritMessage(receiveMsg) {
@@ -553,6 +658,7 @@ class PeerTransfer {
553
658
  }
554
659
  reconnect() {
555
660
  return __awaiter(this, void 0, void 0, function* () {
661
+ console.log('reconnect', this.isPaused);
556
662
  if (this.isPaused) {
557
663
  return;
558
664
  }
@@ -581,20 +687,21 @@ class PeerTransfer {
581
687
  if (this.isPaused) {
582
688
  return;
583
689
  }
690
+ // 已连接且 token 未变,无需重连
584
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)) {
585
692
  return this.peer;
586
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);
587
698
  yield this.reloadConfig();
588
- return this.createPeer();
589
- // return this.createPeer();
699
+ // 优先尝试 peer.reconnect() 轻量重连
590
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) {
591
701
  this.peer.reconnect();
592
702
  return this.peer;
593
703
  }
594
- else {
595
- yield this.reloadConfig();
596
- return this.createPeer();
597
- }
704
+ return this.createPeer();
598
705
  }
599
706
  catch (err) {
600
707
  return this.createPeer();
@@ -603,11 +710,23 @@ class PeerTransfer {
603
710
  }
604
711
  clearInstance() {
605
712
  var _a, _b;
713
+ console.info('clearInstance');
714
+ this.destroyed = true;
606
715
  PeerTransfer.connecting.clear();
607
- 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());
608
722
  for (const pId of peerIds) {
609
723
  this.removeConnection(pId);
610
724
  }
725
+ this.requests.clear();
726
+ for (const timer of this.requestTimers.values()) {
727
+ clearTimeout(timer);
728
+ }
729
+ this.requestTimers.clear();
611
730
  (_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
612
731
  (_b = this.peer) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
613
732
  this.booted = undefined;
@@ -624,9 +743,7 @@ class PeerTransfer {
624
743
  onRequest(conn, message) {
625
744
  return __awaiter(this, void 0, void 0, function* () {
626
745
  try {
627
- // @todo default folder
628
- // console.log('onRequest-->', message.route);
629
- // @todo default folder
746
+ console.log('onRequest@@', conn.peer, message.route);
630
747
  yield this.router.run(conn, message);
631
748
  }
632
749
  catch (err) {
@@ -636,37 +753,35 @@ class PeerTransfer {
636
753
  }
637
754
  onResponse(conn, message) {
638
755
  return __awaiter(this, void 0, void 0, function* () {
639
- // @todo default folder
640
756
  const cb = this.requests.get(message.requestId);
641
757
  if (cb) {
642
758
  cb.apply(this, [message]);
643
759
  this.requests.delete(message.requestId);
644
760
  }
645
- // @todo throw err;
646
761
  });
647
762
  }
648
763
  responseOk(conn, message) {
649
764
  return __awaiter(this, void 0, void 0, function* () {
650
- const msg = {
651
- body: { ok: true },
652
- };
653
- return this.reply(conn, message, msg);
765
+ return this.reply(conn, message, { body: { ok: true } });
654
766
  });
655
767
  }
656
768
  responseFail(conn, message, err) {
657
769
  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);
770
+ return this.reply(conn, message, { body: {}, error: err || 'service error' });
663
771
  });
664
772
  }
665
773
  addRequest(id, cb) {
774
+ // 清除已有定时器,避免重复注册时旧定时器误删新回调
775
+ const oldTimer = this.requestTimers.get(id);
776
+ if (oldTimer) {
777
+ clearTimeout(oldTimer);
778
+ }
666
779
  this.requests.set(id, cb);
667
- setTimeout(() => {
780
+ const timer = setTimeout(() => {
668
781
  this.requests.delete(id);
782
+ this.requestTimers.delete(id);
669
783
  }, this.timeout);
784
+ this.requestTimers.set(id, timer);
670
785
  }
671
786
  use(handler, prefix = '') {
672
787
  this.router.use(handler, prefix);
@@ -680,6 +795,7 @@ class PeerTransfer {
680
795
  }
681
796
  resume() {
682
797
  this.isPaused = false;
798
+ this.destroyed = false;
683
799
  return this.createPeer(true);
684
800
  }
685
801
  }