sx-peerjs-http-util 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -201
- package/dist/MessageHandler.d.ts +8 -0
- package/dist/MessageHandler.d.ts.map +1 -1
- package/dist/PeerJsWrapper.d.ts +89 -3
- package/dist/PeerJsWrapper.d.ts.map +1 -1
- package/dist/Router.d.ts +186 -0
- package/dist/Router.d.ts.map +1 -0
- package/dist/RoutingDB.d.ts +75 -0
- package/dist/RoutingDB.d.ts.map +1 -0
- package/dist/constants.d.ts +23 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +918 -199
- package/dist/index.esm.js.map +3 -3
- package/dist/index.umd.js +918 -199
- package/dist/index.umd.js.map +3 -3
- package/dist/types.d.ts +48 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/Routing.d.ts +0 -88
- package/dist/Routing.d.ts.map +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -146,16 +146,146 @@ var CallSessionImpl = class {
|
|
|
146
146
|
}
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
-
// src/
|
|
150
|
-
var
|
|
149
|
+
// src/constants.ts
|
|
150
|
+
var CONNECTION_TIMEOUT_MS = 3e4;
|
|
151
|
+
var SEND_TIMEOUT_MS = 1e4;
|
|
152
|
+
var RECONNECT_DELAY_MS = 1e3;
|
|
153
|
+
var ROUTE_EXPIRE_AGE_MS = 5 * 60 * 1e3;
|
|
154
|
+
var MAX_DIRECT_NODES = 5;
|
|
155
|
+
var ROUTE_CLEANUP_INTERVAL_MS = 60 * 1e3;
|
|
156
|
+
var ROUTE_BROADCAST_INTERVAL_MS = 30 * 1e3;
|
|
157
|
+
var DEFAULT_TTL = 128;
|
|
158
|
+
|
|
159
|
+
// src/RoutingDB.ts
|
|
160
|
+
var DB_NAME = "peerjs-routing-db";
|
|
161
|
+
var DB_VERSION = 1;
|
|
162
|
+
var ROUTING_TABLE_STORE = "routing-table";
|
|
163
|
+
var DIRECT_NODES_STORE = "direct-nodes";
|
|
164
|
+
var db = null;
|
|
165
|
+
function withStore(storeName, mode, operation) {
|
|
166
|
+
return openDB().then((database) => {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
const tx = database.transaction(storeName, mode);
|
|
169
|
+
const store = tx.objectStore(storeName);
|
|
170
|
+
const request = operation(store);
|
|
171
|
+
if (request) {
|
|
172
|
+
request.onerror = () => reject(request.error);
|
|
173
|
+
request.onsuccess = () => resolve(request.result);
|
|
174
|
+
} else {
|
|
175
|
+
tx.oncomplete = () => resolve(void 0);
|
|
176
|
+
tx.onerror = () => reject(tx.error);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function openDB() {
|
|
182
|
+
return new Promise((resolve, reject) => {
|
|
183
|
+
if (db) {
|
|
184
|
+
resolve(db);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
188
|
+
request.onerror = () => reject(request.error);
|
|
189
|
+
request.onsuccess = () => {
|
|
190
|
+
db = request.result;
|
|
191
|
+
resolve(db);
|
|
192
|
+
};
|
|
193
|
+
request.onupgradeneeded = (e) => {
|
|
194
|
+
const req = e.target;
|
|
195
|
+
const database = req.result;
|
|
196
|
+
if (!database.objectStoreNames.contains(ROUTING_TABLE_STORE)) {
|
|
197
|
+
const routingStore = database.createObjectStore(ROUTING_TABLE_STORE, {
|
|
198
|
+
keyPath: "target"
|
|
199
|
+
});
|
|
200
|
+
routingStore.createIndex("timestamp", "timestamp", { unique: false });
|
|
201
|
+
}
|
|
202
|
+
if (!database.objectStoreNames.contains(DIRECT_NODES_STORE)) {
|
|
203
|
+
const nodesStore = database.createObjectStore(DIRECT_NODES_STORE, {
|
|
204
|
+
keyPath: "nodeId"
|
|
205
|
+
});
|
|
206
|
+
nodesStore.createIndex("timestamp", "timestamp", { unique: false });
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async function initRoutingDB() {
|
|
212
|
+
await openDB();
|
|
213
|
+
}
|
|
214
|
+
async function saveRouteEntry(entry) {
|
|
215
|
+
await withStore(ROUTING_TABLE_STORE, "readwrite", (store) => store.put(entry));
|
|
216
|
+
}
|
|
217
|
+
async function saveRouteEntries(entries) {
|
|
218
|
+
await openDB().then((database) => {
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
const tx = database.transaction(ROUTING_TABLE_STORE, "readwrite");
|
|
221
|
+
const store = tx.objectStore(ROUTING_TABLE_STORE);
|
|
222
|
+
for (const entry of entries) {
|
|
223
|
+
store.put(entry);
|
|
224
|
+
}
|
|
225
|
+
tx.oncomplete = () => resolve();
|
|
226
|
+
tx.onerror = () => reject(tx.error);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
async function deleteRouteEntry(target) {
|
|
231
|
+
await withStore(ROUTING_TABLE_STORE, "readwrite", (store) => store.delete(target));
|
|
232
|
+
}
|
|
233
|
+
async function loadRoutingTable() {
|
|
234
|
+
return withStore(ROUTING_TABLE_STORE, "readonly", (store) => store.getAll());
|
|
235
|
+
}
|
|
236
|
+
async function saveDirectNode(node) {
|
|
237
|
+
await withStore(DIRECT_NODES_STORE, "readwrite", (store) => store.put(node));
|
|
238
|
+
}
|
|
239
|
+
async function saveDirectNodes(nodes) {
|
|
240
|
+
await openDB().then((database) => {
|
|
241
|
+
return new Promise((resolve, reject) => {
|
|
242
|
+
const tx = database.transaction(DIRECT_NODES_STORE, "readwrite");
|
|
243
|
+
const store = tx.objectStore(DIRECT_NODES_STORE);
|
|
244
|
+
for (const node of nodes) {
|
|
245
|
+
store.put(node);
|
|
246
|
+
}
|
|
247
|
+
tx.oncomplete = () => resolve();
|
|
248
|
+
tx.onerror = () => reject(tx.error);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async function loadDirectNodes() {
|
|
253
|
+
return withStore(DIRECT_NODES_STORE, "readonly", (store) => store.getAll());
|
|
254
|
+
}
|
|
255
|
+
async function clearAllRoutingData() {
|
|
256
|
+
const database = await openDB();
|
|
257
|
+
await new Promise((resolve, reject) => {
|
|
258
|
+
const tx = database.transaction(ROUTING_TABLE_STORE, "readwrite");
|
|
259
|
+
const store = tx.objectStore(ROUTING_TABLE_STORE);
|
|
260
|
+
store.clear();
|
|
261
|
+
tx.oncomplete = () => resolve();
|
|
262
|
+
tx.onerror = () => reject(tx.error);
|
|
263
|
+
});
|
|
264
|
+
await new Promise((resolve, reject) => {
|
|
265
|
+
const tx = database.transaction(DIRECT_NODES_STORE, "readwrite");
|
|
266
|
+
const store = tx.objectStore(DIRECT_NODES_STORE);
|
|
267
|
+
store.clear();
|
|
268
|
+
tx.oncomplete = () => resolve();
|
|
269
|
+
tx.onerror = () => reject(tx.error);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/Router.ts
|
|
274
|
+
var Router = class {
|
|
151
275
|
/** 路由表:target -> RouteEntry */
|
|
152
276
|
routingTable = /* @__PURE__ */ new Map();
|
|
153
|
-
/**
|
|
154
|
-
|
|
277
|
+
/** 直连节点及延迟列表 */
|
|
278
|
+
directNodes = [];
|
|
155
279
|
/** 中继配置 */
|
|
156
280
|
relayConfig;
|
|
157
281
|
/** 回调函数集合 */
|
|
158
282
|
callbacks;
|
|
283
|
+
/** 等待路由发现响应的 pending 队列 */
|
|
284
|
+
pendingRouteQueries = /* @__PURE__ */ new Map();
|
|
285
|
+
/** 定时清理定时器 */
|
|
286
|
+
cleanupTimer = null;
|
|
287
|
+
/** 周期广播定时器 */
|
|
288
|
+
broadcastTimer = null;
|
|
159
289
|
/**
|
|
160
290
|
* 创建路由管理器
|
|
161
291
|
* @param callbacks 回调函数集合
|
|
@@ -166,76 +296,243 @@ var RoutingManager = class {
|
|
|
166
296
|
this.relayConfig = relayConfig ?? {};
|
|
167
297
|
}
|
|
168
298
|
/**
|
|
169
|
-
*
|
|
170
|
-
|
|
299
|
+
* 初始化路由管理器(从 IndexedDB 加载数据并启动定时任务)
|
|
300
|
+
*/
|
|
301
|
+
async init() {
|
|
302
|
+
await initRoutingDB();
|
|
303
|
+
await this.loadFromDB();
|
|
304
|
+
this.startMaintenanceTasks();
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 从 IndexedDB 加载路由数据
|
|
308
|
+
*/
|
|
309
|
+
async loadFromDB() {
|
|
310
|
+
try {
|
|
311
|
+
const routes = await loadRoutingTable();
|
|
312
|
+
for (const entry of routes) {
|
|
313
|
+
this.routingTable.set(entry.target, entry);
|
|
314
|
+
}
|
|
315
|
+
this.callbacks.debugLog("Routing", "loadedRoutes", routes.length);
|
|
316
|
+
} catch (e) {
|
|
317
|
+
this.callbacks.debugLog("Routing", "loadError", e);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const nodes = await loadDirectNodes();
|
|
321
|
+
this.directNodes = nodes;
|
|
322
|
+
this.callbacks.debugLog("Routing", "loadedNodes", nodes.length);
|
|
323
|
+
} catch (e) {
|
|
324
|
+
this.callbacks.debugLog("Routing", "loadNodesError", e);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 启动定时维护任务
|
|
329
|
+
*/
|
|
330
|
+
startMaintenanceTasks() {
|
|
331
|
+
this.cleanupTimer = setInterval(() => {
|
|
332
|
+
this.cleanupExpiredEntries();
|
|
333
|
+
}, ROUTE_CLEANUP_INTERVAL_MS);
|
|
334
|
+
this.broadcastTimer = setInterval(() => {
|
|
335
|
+
this.broadcastRouteUpdate();
|
|
336
|
+
}, ROUTE_BROADCAST_INTERVAL_MS);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* 清理过期路由条目
|
|
340
|
+
*/
|
|
341
|
+
async cleanupExpiredEntries() {
|
|
342
|
+
const now = Date.now();
|
|
343
|
+
for (const [target, entry] of this.routingTable) {
|
|
344
|
+
if (now - entry.timestamp > ROUTE_EXPIRE_AGE_MS) {
|
|
345
|
+
this.routingTable.delete(target);
|
|
346
|
+
deleteRouteEntry(target).catch(() => {
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
this.directNodes = this.directNodes.filter((node) => {
|
|
351
|
+
const isExpired = now - node.timestamp > ROUTE_EXPIRE_AGE_MS;
|
|
352
|
+
if (isExpired) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
return true;
|
|
356
|
+
});
|
|
357
|
+
this.callbacks.debugLog("Routing", "cleanup", {
|
|
358
|
+
routes: this.routingTable.size,
|
|
359
|
+
nodes: this.directNodes.length
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* 持久化路由表到 IndexedDB
|
|
364
|
+
*/
|
|
365
|
+
async persist() {
|
|
366
|
+
try {
|
|
367
|
+
const entries = Array.from(this.routingTable.values());
|
|
368
|
+
await saveRouteEntries(entries);
|
|
369
|
+
} catch (e) {
|
|
370
|
+
this.callbacks.debugLog("Routing", "persistError", e);
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
await saveDirectNodes(this.directNodes);
|
|
374
|
+
} catch (e) {
|
|
375
|
+
this.callbacks.debugLog("Routing", "persistNodesError", e);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* 销毁路由管理器(清理定时器)
|
|
380
|
+
*/
|
|
381
|
+
destroy() {
|
|
382
|
+
if (this.cleanupTimer) {
|
|
383
|
+
clearInterval(this.cleanupTimer);
|
|
384
|
+
this.cleanupTimer = null;
|
|
385
|
+
}
|
|
386
|
+
if (this.broadcastTimer) {
|
|
387
|
+
clearInterval(this.broadcastTimer);
|
|
388
|
+
this.broadcastTimer = null;
|
|
389
|
+
}
|
|
390
|
+
this.pendingRouteQueries.forEach((pending) => {
|
|
391
|
+
clearTimeout(pending.timer);
|
|
392
|
+
});
|
|
393
|
+
this.pendingRouteQueries.clear();
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* 记录成功的直连通信
|
|
397
|
+
* @param nodeId 节点 ID
|
|
398
|
+
* @param latency 延迟(毫秒)
|
|
399
|
+
*/
|
|
400
|
+
recordDirectNode(nodeId, latency) {
|
|
401
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
402
|
+
if (nodeId === myPeerId) return;
|
|
403
|
+
const existing = this.directNodes.find((n) => n.nodeId === nodeId);
|
|
404
|
+
const timestamp = Date.now();
|
|
405
|
+
if (existing) {
|
|
406
|
+
existing.latency = latency;
|
|
407
|
+
existing.timestamp = timestamp;
|
|
408
|
+
} else {
|
|
409
|
+
const maxRelayNodes = this.relayConfig.maxRelayNodes ?? MAX_DIRECT_NODES;
|
|
410
|
+
this.directNodes.push({ nodeId, latency, timestamp });
|
|
411
|
+
if (this.directNodes.length > maxRelayNodes) {
|
|
412
|
+
this.directNodes.sort((a, b) => a.latency - b.latency);
|
|
413
|
+
this.directNodes.shift();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
this.callbacks.debugLog("Routing", "directNode", { nodeId, latency });
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* 获取直连节点列表(按延迟升序)
|
|
420
|
+
* @returns 直连节点列表
|
|
421
|
+
*/
|
|
422
|
+
getDirectNodes() {
|
|
423
|
+
return [...this.directNodes].sort((a, b) => a.latency - b.latency);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* 检查是否可以直连目标节点
|
|
427
|
+
* @param targetId 目标节点 ID
|
|
428
|
+
* @returns 是否可以直连
|
|
429
|
+
*/
|
|
430
|
+
canReachDirectly(targetId) {
|
|
431
|
+
return this.directNodes.some((n) => n.nodeId === targetId);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* 获取到直连节点的延迟
|
|
435
|
+
* @param nodeId 节点 ID
|
|
436
|
+
* @returns 延迟(毫秒),如果不存在返回 null
|
|
437
|
+
*/
|
|
438
|
+
getDirectLatency(nodeId) {
|
|
439
|
+
const node = this.directNodes.find((n) => n.nodeId === nodeId);
|
|
440
|
+
return node ? node.latency : null;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* 移除失效的路由(通信失败时调用)
|
|
444
|
+
* @param nodeId 失效的节点 ID
|
|
445
|
+
*/
|
|
446
|
+
removeRoute(nodeId) {
|
|
447
|
+
let removed = false;
|
|
448
|
+
for (const [target, entry] of this.routingTable) {
|
|
449
|
+
const originalLength = entry.nextHops.length;
|
|
450
|
+
entry.nextHops = entry.nextHops.filter((h) => h.nodeId !== nodeId);
|
|
451
|
+
if (entry.nextHops.length === 0) {
|
|
452
|
+
this.routingTable.delete(target);
|
|
453
|
+
deleteRouteEntry(target).catch(() => {
|
|
454
|
+
});
|
|
455
|
+
removed = true;
|
|
456
|
+
} else if (entry.nextHops.length < originalLength) {
|
|
457
|
+
entry.timestamp = Date.now();
|
|
458
|
+
saveRouteEntry(entry).catch(() => {
|
|
459
|
+
});
|
|
460
|
+
removed = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const nodeIndex = this.directNodes.findIndex((n) => n.nodeId === nodeId);
|
|
464
|
+
if (nodeIndex !== -1) {
|
|
465
|
+
this.directNodes.splice(nodeIndex, 1);
|
|
466
|
+
removed = true;
|
|
467
|
+
}
|
|
468
|
+
if (removed) {
|
|
469
|
+
this.callbacks.debugLog("Routing", "routeRemoved", nodeId);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* 检查路由表是否为空
|
|
474
|
+
* @returns 是否为空
|
|
475
|
+
*/
|
|
476
|
+
isRoutingTableEmpty() {
|
|
477
|
+
return this.routingTable.size === 0;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* 记录成功通信的节点(兼容旧接口)
|
|
171
481
|
* @param nodeId 节点 ID
|
|
172
482
|
*/
|
|
173
483
|
recordSuccessfulNode(nodeId) {
|
|
174
484
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
175
485
|
if (nodeId === myPeerId) return;
|
|
176
|
-
|
|
486
|
+
const existing = this.directNodes.find((n) => n.nodeId === nodeId);
|
|
487
|
+
if (!existing) {
|
|
177
488
|
const maxRelayNodes = this.relayConfig.maxRelayNodes ?? 5;
|
|
178
|
-
this.
|
|
179
|
-
if (this.
|
|
180
|
-
this.
|
|
489
|
+
this.directNodes.push({ nodeId, latency: 100, timestamp: Date.now() });
|
|
490
|
+
if (this.directNodes.length > maxRelayNodes) {
|
|
491
|
+
this.directNodes.sort((a, b) => a.latency - b.latency);
|
|
492
|
+
this.directNodes.shift();
|
|
181
493
|
}
|
|
182
|
-
this.callbacks.debugLog("Routing", "newNode", nodeId);
|
|
183
494
|
}
|
|
184
495
|
}
|
|
185
496
|
/**
|
|
186
497
|
* 广播路由更新
|
|
187
|
-
*
|
|
498
|
+
* 向所有直连节点发送路由更新消息,告知它们本节点可达的节点列表
|
|
188
499
|
*/
|
|
189
500
|
async broadcastRouteUpdate() {
|
|
190
501
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
191
|
-
const reachableNodes =
|
|
192
|
-
for (const
|
|
502
|
+
const reachableNodes = this.getReachableNodes();
|
|
503
|
+
for (const node of this.directNodes) {
|
|
193
504
|
try {
|
|
194
|
-
await this.sendRouteUpdate(nodeId, reachableNodes);
|
|
505
|
+
await this.sendRouteUpdate(node.nodeId, reachableNodes);
|
|
195
506
|
} catch {
|
|
196
507
|
}
|
|
197
508
|
}
|
|
198
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* 获取本节点可达的节点列表
|
|
512
|
+
* @returns 可达节点数组(直连节点 + 自己)
|
|
513
|
+
*/
|
|
514
|
+
getReachableNodes() {
|
|
515
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
516
|
+
const directNodeIds = this.directNodes.map((n) => n.nodeId);
|
|
517
|
+
return [.../* @__PURE__ */ new Set([...directNodeIds, myPeerId])];
|
|
518
|
+
}
|
|
199
519
|
/**
|
|
200
520
|
* 发送路由更新到指定节点
|
|
201
521
|
* @param targetId 目标节点 ID
|
|
202
522
|
* @param reachableNodes 可达的节点列表
|
|
203
523
|
*/
|
|
204
524
|
async sendRouteUpdate(targetId, reachableNodes) {
|
|
205
|
-
const peerInstance = this.callbacks.getPeerInstance();
|
|
206
525
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const message = {
|
|
218
|
-
type: "route-update",
|
|
219
|
-
id: `${myPeerId}-route-${Date.now()}`,
|
|
220
|
-
originalTarget: targetId,
|
|
221
|
-
relayPath: [],
|
|
222
|
-
forwardPath: [],
|
|
223
|
-
routeUpdate: { reachableNodes }
|
|
224
|
-
};
|
|
225
|
-
conn.send(message);
|
|
226
|
-
clearTimeout(timeout);
|
|
227
|
-
conn.close();
|
|
228
|
-
resolve();
|
|
229
|
-
});
|
|
230
|
-
conn.on("error", () => {
|
|
231
|
-
clearTimeout(timeout);
|
|
232
|
-
reject(new Error("Route update failed"));
|
|
233
|
-
});
|
|
234
|
-
conn.on("close", () => {
|
|
235
|
-
clearTimeout(timeout);
|
|
236
|
-
resolve();
|
|
237
|
-
});
|
|
238
|
-
});
|
|
526
|
+
const message = {
|
|
527
|
+
type: "route-update",
|
|
528
|
+
id: `${myPeerId}-route-${Date.now()}`,
|
|
529
|
+
originalTarget: targetId,
|
|
530
|
+
relayPath: [],
|
|
531
|
+
forwardPath: [],
|
|
532
|
+
ttl: DEFAULT_TTL,
|
|
533
|
+
routeUpdate: { reachableNodes }
|
|
534
|
+
};
|
|
535
|
+
await this.callbacks.sendRelayMessage(targetId, message);
|
|
239
536
|
}
|
|
240
537
|
/**
|
|
241
538
|
* 处理收到的路由更新
|
|
@@ -248,39 +545,217 @@ var RoutingManager = class {
|
|
|
248
545
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
249
546
|
const { reachableNodes } = message.routeUpdate;
|
|
250
547
|
const timestamp = Date.now();
|
|
548
|
+
const viaLatency = this.getDirectLatency(fromPeerId) ?? 100;
|
|
251
549
|
for (const target of reachableNodes) {
|
|
252
550
|
if (target === myPeerId) continue;
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
if (!
|
|
256
|
-
|
|
551
|
+
let entry = this.routingTable.get(target);
|
|
552
|
+
const totalLatency = viaLatency + 100;
|
|
553
|
+
if (!entry) {
|
|
554
|
+
entry = {
|
|
257
555
|
target,
|
|
258
|
-
|
|
259
|
-
hops,
|
|
260
|
-
via: fromPeerId,
|
|
556
|
+
nextHops: [],
|
|
557
|
+
hops: 1,
|
|
261
558
|
timestamp
|
|
559
|
+
};
|
|
560
|
+
this.routingTable.set(target, entry);
|
|
561
|
+
}
|
|
562
|
+
const existingHop = entry.nextHops.find((h) => h.nodeId === fromPeerId);
|
|
563
|
+
if (existingHop) {
|
|
564
|
+
existingHop.latency = totalLatency;
|
|
565
|
+
} else {
|
|
566
|
+
entry.nextHops.push({ nodeId: fromPeerId, latency: totalLatency });
|
|
567
|
+
}
|
|
568
|
+
entry.nextHops.sort((a, b) => a.latency - b.latency);
|
|
569
|
+
entry.hops = Math.min(entry.hops, 1);
|
|
570
|
+
entry.timestamp = timestamp;
|
|
571
|
+
this.callbacks.debugLog("Routing", "update", { target, nextHop: fromPeerId, latency: totalLatency });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* 执行路由发现广播
|
|
576
|
+
* 当直连和路由表都失败时,向所有直连节点广播询问谁能连通目标
|
|
577
|
+
* @param targetId 目标节点 ID
|
|
578
|
+
* @returns 路由条目(如果发现)
|
|
579
|
+
*/
|
|
580
|
+
async discoverRoute(targetId) {
|
|
581
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
582
|
+
const directNodes = this.getDirectNodes();
|
|
583
|
+
if (directNodes.length === 0) {
|
|
584
|
+
this.callbacks.debugLog("Routing", "discoverRoute", "no direct nodes");
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
this.callbacks.debugLog("Routing", "discoverRoute", { targetId, directNodes: directNodes.length });
|
|
588
|
+
const queryId = `${myPeerId}-query-${Date.now()}`;
|
|
589
|
+
const routeEntry = await new Promise((resolve, reject) => {
|
|
590
|
+
const timer = setTimeout(() => {
|
|
591
|
+
this.pendingRouteQueries.delete(queryId);
|
|
592
|
+
resolve(null);
|
|
593
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
594
|
+
this.pendingRouteQueries.set(queryId, { resolve, reject, timer });
|
|
595
|
+
const message = {
|
|
596
|
+
type: "route-query",
|
|
597
|
+
id: queryId,
|
|
598
|
+
originalTarget: targetId,
|
|
599
|
+
relayPath: [myPeerId],
|
|
600
|
+
forwardPath: [],
|
|
601
|
+
ttl: DEFAULT_TTL,
|
|
602
|
+
routeQuery: {
|
|
603
|
+
queryOrigin: myPeerId,
|
|
604
|
+
targetNode: targetId,
|
|
605
|
+
queryPath: [myPeerId]
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
for (const node of directNodes) {
|
|
609
|
+
this.callbacks.sendRelayMessage(node.nodeId, message).catch(() => {
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
return routeEntry;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* 处理路由查询消息
|
|
617
|
+
* @param fromPeerId 发送查询的节点
|
|
618
|
+
* @param message 路由查询消息
|
|
619
|
+
*/
|
|
620
|
+
handleRouteQuery(fromPeerId, message) {
|
|
621
|
+
if (!message.routeQuery) return;
|
|
622
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
623
|
+
const { queryOrigin, targetNode, queryPath } = message.routeQuery;
|
|
624
|
+
if (targetNode === myPeerId) {
|
|
625
|
+
const latency = this.getDirectLatency(fromPeerId) ?? 100;
|
|
626
|
+
const response = {
|
|
627
|
+
type: "route-response",
|
|
628
|
+
id: `${myPeerId}-resp-${Date.now()}`,
|
|
629
|
+
originalTarget: queryOrigin,
|
|
630
|
+
relayPath: [],
|
|
631
|
+
forwardPath: [],
|
|
632
|
+
ttl: DEFAULT_TTL,
|
|
633
|
+
routeResponse: {
|
|
634
|
+
queryOrigin,
|
|
635
|
+
responder: myPeerId,
|
|
636
|
+
targetNode,
|
|
637
|
+
latency
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
this.callbacks.sendRelayMessage(fromPeerId, response);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (queryPath.includes(myPeerId)) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const currentTTL = message.ttl ?? DEFAULT_TTL;
|
|
647
|
+
if (currentTTL <= 0) {
|
|
648
|
+
this.callbacks.debugLog("Routing", "ttlExpired", { type: "route-query", id: message.id });
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const nextHop = this.findNextHopToTarget(targetNode);
|
|
652
|
+
if (nextHop) {
|
|
653
|
+
const latency = (this.getDirectLatency(fromPeerId) ?? 100) + nextHop.latency;
|
|
654
|
+
const response = {
|
|
655
|
+
type: "route-response",
|
|
656
|
+
id: `${myPeerId}-resp-${Date.now()}`,
|
|
657
|
+
originalTarget: queryOrigin,
|
|
658
|
+
relayPath: [],
|
|
659
|
+
forwardPath: [],
|
|
660
|
+
ttl: DEFAULT_TTL,
|
|
661
|
+
routeResponse: {
|
|
662
|
+
queryOrigin,
|
|
663
|
+
responder: myPeerId,
|
|
664
|
+
targetNode,
|
|
665
|
+
latency
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
this.callbacks.sendRelayMessage(fromPeerId, response);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const newPath = [...queryPath, myPeerId];
|
|
672
|
+
const forwardMessage = {
|
|
673
|
+
...message,
|
|
674
|
+
relayPath: newPath,
|
|
675
|
+
ttl: currentTTL - 1,
|
|
676
|
+
routeQuery: {
|
|
677
|
+
...message.routeQuery,
|
|
678
|
+
queryPath: newPath
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
for (const node of this.directNodes) {
|
|
682
|
+
if (node.nodeId !== fromPeerId) {
|
|
683
|
+
this.callbacks.sendRelayMessage(node.nodeId, forwardMessage).catch(() => {
|
|
262
684
|
});
|
|
263
|
-
this.callbacks.debugLog("Routing", "update", { target, nextHop: fromPeerId, hops });
|
|
264
685
|
}
|
|
265
686
|
}
|
|
266
687
|
}
|
|
267
688
|
/**
|
|
268
|
-
*
|
|
689
|
+
* 处理路由查询响应
|
|
690
|
+
* @param fromPeerId 响应者节点
|
|
691
|
+
* @param message 路由响应消息
|
|
692
|
+
*/
|
|
693
|
+
handleRouteResponse(fromPeerId, message) {
|
|
694
|
+
if (!message.routeResponse) return;
|
|
695
|
+
const { queryOrigin, targetNode, latency } = message.routeResponse;
|
|
696
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
697
|
+
if (queryOrigin !== myPeerId) return;
|
|
698
|
+
const pending = Array.from(this.pendingRouteQueries.values())[0];
|
|
699
|
+
if (!pending) return;
|
|
700
|
+
let entry = this.routingTable.get(targetNode);
|
|
701
|
+
const timestamp = Date.now();
|
|
702
|
+
if (!entry) {
|
|
703
|
+
entry = {
|
|
704
|
+
target: targetNode,
|
|
705
|
+
nextHops: [],
|
|
706
|
+
hops: 1,
|
|
707
|
+
timestamp
|
|
708
|
+
};
|
|
709
|
+
this.routingTable.set(targetNode, entry);
|
|
710
|
+
}
|
|
711
|
+
const existingHop = entry.nextHops.find((h) => h.nodeId === fromPeerId);
|
|
712
|
+
if (existingHop) {
|
|
713
|
+
existingHop.latency = latency;
|
|
714
|
+
} else {
|
|
715
|
+
entry.nextHops.push({ nodeId: fromPeerId, latency });
|
|
716
|
+
}
|
|
717
|
+
entry.nextHops.sort((a, b) => a.latency - b.latency);
|
|
718
|
+
entry.timestamp = timestamp;
|
|
719
|
+
this.callbacks.debugLog("Routing", "discovered", { targetNode, nextHop: fromPeerId, latency });
|
|
720
|
+
clearTimeout(pending.timer);
|
|
721
|
+
pending.resolve(entry);
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* 查找到目标节点的下一跳
|
|
725
|
+
* @param targetId 目标节点 ID
|
|
726
|
+
* @returns 下一跳信息,如果没有则返回 null
|
|
727
|
+
*/
|
|
728
|
+
findNextHopToTarget(targetId) {
|
|
729
|
+
const entry = this.routingTable.get(targetId);
|
|
730
|
+
if (!entry || entry.nextHops.length === 0) return null;
|
|
731
|
+
return entry.nextHops[0];
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* 获取到目标节点的所有下一跳(按延迟升序)
|
|
735
|
+
* @param targetId 目标节点 ID
|
|
736
|
+
* @returns 下一跳列表
|
|
737
|
+
*/
|
|
738
|
+
getNextHopsToTarget(targetId) {
|
|
739
|
+
const entry = this.routingTable.get(targetId);
|
|
740
|
+
return entry ? [...entry.nextHops] : [];
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* 获取路由表
|
|
269
744
|
* @returns 路由表对象
|
|
270
745
|
*/
|
|
271
746
|
getRoutingTable() {
|
|
272
747
|
const result = {};
|
|
273
748
|
this.routingTable.forEach((entry, target) => {
|
|
274
|
-
result[target] = entry;
|
|
749
|
+
result[target] = { ...entry, nextHops: [...entry.nextHops] };
|
|
275
750
|
});
|
|
276
751
|
return result;
|
|
277
752
|
}
|
|
278
753
|
/**
|
|
279
|
-
*
|
|
280
|
-
* @returns
|
|
754
|
+
* 获取已知节点列表(兼容旧接口)
|
|
755
|
+
* @returns 节点 ID 数组
|
|
281
756
|
*/
|
|
282
757
|
getKnownNodes() {
|
|
283
|
-
return
|
|
758
|
+
return this.directNodes.map((n) => n.nodeId);
|
|
284
759
|
}
|
|
285
760
|
};
|
|
286
761
|
|
|
@@ -339,6 +814,14 @@ var MessageHandler = class {
|
|
|
339
814
|
}
|
|
340
815
|
return { status: 200, data: result };
|
|
341
816
|
}
|
|
817
|
+
const currentTTL = relayMessage.ttl ?? DEFAULT_TTL;
|
|
818
|
+
if (currentTTL <= 0) {
|
|
819
|
+
this.callbacks.debugLog("MessageHandler", "ttlExpired", { type: "relay-request", id: relayMessage.id });
|
|
820
|
+
return {
|
|
821
|
+
status: 502,
|
|
822
|
+
data: { error: "TTL expired - message dropped to prevent routing loop" }
|
|
823
|
+
};
|
|
824
|
+
}
|
|
342
825
|
if (forwardPath.length > 0) {
|
|
343
826
|
const nextHop = forwardPath[0];
|
|
344
827
|
const remainingPath = forwardPath.slice(1);
|
|
@@ -349,6 +832,7 @@ var MessageHandler = class {
|
|
|
349
832
|
originalTarget,
|
|
350
833
|
relayPath: [...relayPath, myPeerId],
|
|
351
834
|
forwardPath: remainingPath,
|
|
835
|
+
ttl: currentTTL - 1,
|
|
352
836
|
request
|
|
353
837
|
});
|
|
354
838
|
return response;
|
|
@@ -391,54 +875,70 @@ var MessageHandler = class {
|
|
|
391
875
|
return typeof result === "object" && result !== null && "status" in result && "data" in result;
|
|
392
876
|
}
|
|
393
877
|
/**
|
|
394
|
-
*
|
|
395
|
-
* @param
|
|
396
|
-
* @param message
|
|
397
|
-
* @
|
|
878
|
+
* 创建连接并发送中继消息的通用方法
|
|
879
|
+
* @param targetId 目标节点 ID
|
|
880
|
+
* @param message 要发送的消息
|
|
881
|
+
* @param extractResponse 从响应消息中提取数据的函数
|
|
882
|
+
* @returns Promise<Response>
|
|
398
883
|
*/
|
|
399
|
-
|
|
884
|
+
createConnectionAndSend(targetId, message, extractResponse) {
|
|
400
885
|
return new Promise((resolve, reject) => {
|
|
401
|
-
this.callbacks.debugLog("MessageHandler", "
|
|
886
|
+
this.callbacks.debugLog("MessageHandler", "createConnectionAndSend", { targetId });
|
|
402
887
|
this.callbacks.waitForReady().then(() => {
|
|
403
888
|
const peerInstance = this.callbacks.getPeerInstance();
|
|
404
889
|
if (!peerInstance) {
|
|
405
890
|
reject(new Error("Peer instance not available"));
|
|
406
891
|
return;
|
|
407
892
|
}
|
|
408
|
-
const conn = peerInstance.connect(
|
|
893
|
+
const conn = peerInstance.connect(targetId, { reliable: true });
|
|
409
894
|
const timeout = setTimeout(() => {
|
|
410
895
|
conn.close();
|
|
411
|
-
reject(new Error(`
|
|
412
|
-
},
|
|
896
|
+
reject(new Error(`Connection timeout: ${targetId}`));
|
|
897
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
413
898
|
conn.on("open", () => {
|
|
414
|
-
this.callbacks.debugLog("Conn", "open",
|
|
899
|
+
this.callbacks.debugLog("Conn", "open", targetId);
|
|
415
900
|
conn.send(message);
|
|
416
901
|
});
|
|
417
902
|
conn.on("data", (responseData) => {
|
|
418
903
|
const response = responseData;
|
|
419
|
-
|
|
904
|
+
const extracted = extractResponse(response);
|
|
905
|
+
if (extracted) {
|
|
420
906
|
clearTimeout(timeout);
|
|
421
907
|
conn.close();
|
|
422
|
-
|
|
423
|
-
resolve(response.response);
|
|
424
|
-
} else {
|
|
425
|
-
reject(new Error("Invalid relay response"));
|
|
426
|
-
}
|
|
908
|
+
resolve(extracted);
|
|
427
909
|
}
|
|
428
910
|
});
|
|
429
911
|
conn.on("error", (err) => {
|
|
430
|
-
this.callbacks.debugLog("Conn", "error", { peer:
|
|
912
|
+
this.callbacks.debugLog("Conn", "error", { peer: targetId, error: err });
|
|
431
913
|
clearTimeout(timeout);
|
|
432
914
|
reject(err);
|
|
433
915
|
});
|
|
434
916
|
conn.on("close", () => {
|
|
435
|
-
this.callbacks.debugLog("Conn", "close",
|
|
917
|
+
this.callbacks.debugLog("Conn", "close", targetId);
|
|
436
918
|
clearTimeout(timeout);
|
|
437
|
-
reject(new Error("
|
|
919
|
+
reject(new Error("Connection closed"));
|
|
438
920
|
});
|
|
439
921
|
}).catch(reject);
|
|
440
922
|
});
|
|
441
923
|
}
|
|
924
|
+
/**
|
|
925
|
+
* 转发中继请求到下一个节点
|
|
926
|
+
* @param nextHop 下一跳节点 ID
|
|
927
|
+
* @param message 要转发的消息
|
|
928
|
+
* @returns 响应数据
|
|
929
|
+
*/
|
|
930
|
+
async forwardRelay(nextHop, message) {
|
|
931
|
+
return this.createConnectionAndSend(
|
|
932
|
+
nextHop,
|
|
933
|
+
message,
|
|
934
|
+
(response) => {
|
|
935
|
+
if (response.type === "relay-response" && response.response) {
|
|
936
|
+
return response.response;
|
|
937
|
+
}
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
);
|
|
941
|
+
}
|
|
442
942
|
/**
|
|
443
943
|
* 转发到最终目标节点(当没有更多中继节点时使用)
|
|
444
944
|
* @param targetId 目标节点 ID
|
|
@@ -448,65 +948,37 @@ var MessageHandler = class {
|
|
|
448
948
|
*/
|
|
449
949
|
async forwardToTarget(targetId, request, originalMessage) {
|
|
450
950
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
951
|
+
const currentTTL = originalMessage.ttl ?? DEFAULT_TTL;
|
|
952
|
+
const message = {
|
|
953
|
+
type: "relay-request",
|
|
954
|
+
id: originalMessage.id,
|
|
955
|
+
originalTarget: originalMessage.originalTarget,
|
|
956
|
+
relayPath: [...originalMessage.relayPath, myPeerId],
|
|
957
|
+
forwardPath: [],
|
|
958
|
+
ttl: currentTTL - 1,
|
|
959
|
+
request
|
|
960
|
+
};
|
|
961
|
+
const response = await this.createConnectionAndSend(
|
|
962
|
+
targetId,
|
|
963
|
+
message,
|
|
964
|
+
(resp) => {
|
|
965
|
+
if (resp.type === "relay-response" && resp.response) {
|
|
966
|
+
return resp.response;
|
|
458
967
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}, 3e4);
|
|
464
|
-
conn.on("open", () => {
|
|
465
|
-
this.callbacks.debugLog("Conn", "open", targetId);
|
|
466
|
-
const message = {
|
|
467
|
-
type: "relay-request",
|
|
468
|
-
id: originalMessage.id,
|
|
469
|
-
originalTarget: originalMessage.originalTarget,
|
|
470
|
-
relayPath: [...originalMessage.relayPath, myPeerId],
|
|
471
|
-
forwardPath: [],
|
|
472
|
-
request
|
|
473
|
-
};
|
|
474
|
-
conn.send(message);
|
|
475
|
-
});
|
|
476
|
-
conn.on("data", (responseData) => {
|
|
477
|
-
const response = responseData;
|
|
478
|
-
if (response.type === "relay-response") {
|
|
479
|
-
clearTimeout(timeout);
|
|
480
|
-
conn.close();
|
|
481
|
-
if (response.response) {
|
|
482
|
-
resolve(response.response.data);
|
|
483
|
-
} else {
|
|
484
|
-
reject(new Error("Invalid relay response"));
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
conn.on("error", (err) => {
|
|
489
|
-
this.callbacks.debugLog("Conn", "error", { peer: targetId, error: err });
|
|
490
|
-
clearTimeout(timeout);
|
|
491
|
-
reject(err);
|
|
492
|
-
});
|
|
493
|
-
conn.on("close", () => {
|
|
494
|
-
this.callbacks.debugLog("Conn", "close", targetId);
|
|
495
|
-
clearTimeout(timeout);
|
|
496
|
-
reject(new Error("Forward connection closed"));
|
|
497
|
-
});
|
|
498
|
-
}).catch(reject);
|
|
499
|
-
});
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
);
|
|
971
|
+
return response.data;
|
|
500
972
|
}
|
|
501
973
|
};
|
|
502
974
|
|
|
503
975
|
// src/PeerJsWrapper.ts
|
|
504
|
-
var VERSION = "1.
|
|
976
|
+
var VERSION = "1.2.0";
|
|
505
977
|
console.log(`[sx-peerjs-http-util] v${VERSION}`);
|
|
506
978
|
function generateUUID() {
|
|
507
979
|
return crypto.randomUUID();
|
|
508
980
|
}
|
|
509
|
-
var PeerJsWrapper = class {
|
|
981
|
+
var PeerJsWrapper = class _PeerJsWrapper {
|
|
510
982
|
/** 本地 Peer ID */
|
|
511
983
|
myPeerId;
|
|
512
984
|
/** PeerJS 实例 */
|
|
@@ -530,7 +1002,7 @@ var PeerJsWrapper = class {
|
|
|
530
1002
|
/** 来电监听器集合 */
|
|
531
1003
|
incomingCallListeners = /* @__PURE__ */ new Set();
|
|
532
1004
|
/** 路由管理器 */
|
|
533
|
-
|
|
1005
|
+
router;
|
|
534
1006
|
/** 消息处理器 */
|
|
535
1007
|
messageHandler;
|
|
536
1008
|
/**
|
|
@@ -547,17 +1019,32 @@ var PeerJsWrapper = class {
|
|
|
547
1019
|
const callbacks = {
|
|
548
1020
|
getMyPeerId: () => this.myPeerId,
|
|
549
1021
|
getPeerInstance: () => this.peerInstance,
|
|
550
|
-
debugLog: this.debugLog.bind(this)
|
|
1022
|
+
debugLog: this.debugLog.bind(this),
|
|
1023
|
+
sendRelayMessage: (targetId, message) => this.sendRelayMessage(targetId, message)
|
|
551
1024
|
};
|
|
552
|
-
this.
|
|
1025
|
+
this.router = new Router(callbacks, relayConfig);
|
|
1026
|
+
this.router.init();
|
|
553
1027
|
this.messageHandler = new MessageHandler({
|
|
554
1028
|
...callbacks,
|
|
555
1029
|
waitForReady: () => this.waitForReady(),
|
|
556
1030
|
getSimpleHandlers: () => this.simpleHandlers,
|
|
557
|
-
onRouteUpdate: (fromPeerId, message) => this.
|
|
1031
|
+
onRouteUpdate: (fromPeerId, message) => this.router.handleRouteUpdate(fromPeerId, message)
|
|
558
1032
|
});
|
|
559
1033
|
this.connect();
|
|
560
1034
|
}
|
|
1035
|
+
/**
|
|
1036
|
+
* 创建实例并等待就绪(语法糖)
|
|
1037
|
+
* @param peerId 可选的 Peer ID
|
|
1038
|
+
* @param isDebug 是否开启调试模式
|
|
1039
|
+
* @param server 可选的信令服务器配置
|
|
1040
|
+
* @param relayConfig 可选的中继配置
|
|
1041
|
+
* @returns Promise<PeerJsWrapper>
|
|
1042
|
+
*/
|
|
1043
|
+
static async create(peerId, isDebug, server, relayConfig) {
|
|
1044
|
+
const wrapper = new _PeerJsWrapper(peerId, isDebug, server, relayConfig);
|
|
1045
|
+
await wrapper.whenReady();
|
|
1046
|
+
return wrapper;
|
|
1047
|
+
}
|
|
561
1048
|
debugLog(obj, event, data) {
|
|
562
1049
|
if (this.isDebug) {
|
|
563
1050
|
console.log(obj, event, data);
|
|
@@ -601,7 +1088,7 @@ var PeerJsWrapper = class {
|
|
|
601
1088
|
this.reconnectTimer = setTimeout(() => {
|
|
602
1089
|
this.reconnectTimer = null;
|
|
603
1090
|
this.reconnect();
|
|
604
|
-
},
|
|
1091
|
+
}, RECONNECT_DELAY_MS);
|
|
605
1092
|
}
|
|
606
1093
|
reconnect() {
|
|
607
1094
|
if (this.isDestroyed) return;
|
|
@@ -645,32 +1132,149 @@ var PeerJsWrapper = class {
|
|
|
645
1132
|
this.peerInstance.on("error", onError);
|
|
646
1133
|
});
|
|
647
1134
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
1135
|
+
getRoutingTable() {
|
|
1136
|
+
return this.router.getRoutingTable();
|
|
1137
|
+
}
|
|
1138
|
+
getKnownNodes() {
|
|
1139
|
+
return this.router.getKnownNodes();
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* 发送中继消息的辅助方法
|
|
1143
|
+
* @param targetId 目标节点 ID
|
|
1144
|
+
* @param message 中继消息
|
|
1145
|
+
*/
|
|
1146
|
+
sendRelayMessage(targetId, message) {
|
|
653
1147
|
return new Promise((resolve, reject) => {
|
|
654
|
-
this.
|
|
1148
|
+
if (!this.peerInstance) {
|
|
1149
|
+
reject(new Error("Peer instance not available"));
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const conn = this.peerInstance.connect(targetId, { reliable: true });
|
|
1153
|
+
const timeout = setTimeout(() => {
|
|
1154
|
+
conn.close();
|
|
1155
|
+
reject(new Error(`Send to ${targetId} timeout`));
|
|
1156
|
+
}, SEND_TIMEOUT_MS);
|
|
1157
|
+
conn.on("open", () => {
|
|
1158
|
+
conn.send(message);
|
|
1159
|
+
clearTimeout(timeout);
|
|
1160
|
+
conn.close();
|
|
1161
|
+
resolve();
|
|
1162
|
+
});
|
|
1163
|
+
conn.on("error", () => {
|
|
1164
|
+
clearTimeout(timeout);
|
|
1165
|
+
reject(new Error(`Send to ${targetId} failed`));
|
|
1166
|
+
});
|
|
1167
|
+
conn.on("close", () => {
|
|
1168
|
+
clearTimeout(timeout);
|
|
1169
|
+
resolve();
|
|
1170
|
+
});
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* 尝试直连目标节点
|
|
1175
|
+
* @param targetId 目标节点 ID
|
|
1176
|
+
* @param path 请求路径
|
|
1177
|
+
* @param data 请求数据
|
|
1178
|
+
* @param requestId 请求 ID
|
|
1179
|
+
* @returns Promise<unknown> - 响应数据
|
|
1180
|
+
*/
|
|
1181
|
+
tryDirectConnect(targetId, path, data, requestId) {
|
|
1182
|
+
return new Promise((resolve, reject) => {
|
|
1183
|
+
if (!this.peerInstance) {
|
|
1184
|
+
reject(new Error("Peer instance not available"));
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const startTime = Date.now();
|
|
1188
|
+
const conn = this.peerInstance.connect(targetId, { reliable: true });
|
|
1189
|
+
let resolved = false;
|
|
1190
|
+
const timeout = setTimeout(() => {
|
|
1191
|
+
if (!resolved) {
|
|
1192
|
+
resolved = true;
|
|
1193
|
+
conn.close();
|
|
1194
|
+
reject(new Error(`Request timeout: ${targetId}${path}`));
|
|
1195
|
+
}
|
|
1196
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
1197
|
+
conn.on("open", () => {
|
|
1198
|
+
const latency = Date.now() - startTime;
|
|
1199
|
+
this.debugLog("Conn", "open", { peer: targetId, latency });
|
|
1200
|
+
const request = { path, data };
|
|
1201
|
+
const message = {
|
|
1202
|
+
type: "request",
|
|
1203
|
+
id: requestId,
|
|
1204
|
+
request
|
|
1205
|
+
};
|
|
1206
|
+
conn.send(message);
|
|
1207
|
+
});
|
|
1208
|
+
conn.on("data", (responseData) => {
|
|
1209
|
+
this.debugLog("Conn", "data", { peer: targetId, data: responseData });
|
|
1210
|
+
const message = responseData;
|
|
1211
|
+
if (message.type === "response" && message.id === requestId) {
|
|
1212
|
+
if (!resolved) {
|
|
1213
|
+
resolved = true;
|
|
1214
|
+
clearTimeout(timeout);
|
|
1215
|
+
const response = message.response;
|
|
1216
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1217
|
+
conn.close();
|
|
1218
|
+
reject(new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`));
|
|
1219
|
+
} else {
|
|
1220
|
+
const latency = Date.now() - startTime;
|
|
1221
|
+
this.router.recordDirectNode(targetId, latency);
|
|
1222
|
+
this.router.broadcastRouteUpdate();
|
|
1223
|
+
resolve(response.data);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
conn.on("error", (err) => {
|
|
1229
|
+
if (!resolved) {
|
|
1230
|
+
resolved = true;
|
|
1231
|
+
clearTimeout(timeout);
|
|
1232
|
+
this.debugLog("Conn", "error", { peer: targetId, error: err });
|
|
1233
|
+
reject(err);
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
conn.on("close", () => {
|
|
1237
|
+
if (!resolved) {
|
|
1238
|
+
resolved = true;
|
|
1239
|
+
clearTimeout(timeout);
|
|
1240
|
+
this.debugLog("Conn", "close", targetId);
|
|
1241
|
+
reject(new Error("Connection closed"));
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* 通过中继节点转发请求
|
|
1248
|
+
* @param nextHopId 下一跳节点 ID
|
|
1249
|
+
* @param targetId 原始目标节点 ID
|
|
1250
|
+
* @param path 请求路径
|
|
1251
|
+
* @param data 请求数据
|
|
1252
|
+
* @returns Promise<unknown> - 响应数据
|
|
1253
|
+
*/
|
|
1254
|
+
relayVia(nextHopId, targetId, path, data) {
|
|
1255
|
+
return new Promise((resolve, reject) => {
|
|
1256
|
+
this.debugLog("PeerJsWrapper", "relayVia", { targetId, nextHop: nextHopId });
|
|
655
1257
|
this.waitForReady().then(() => {
|
|
656
1258
|
if (!this.peerInstance) {
|
|
657
1259
|
reject(new Error("Peer instance not available"));
|
|
658
1260
|
return;
|
|
659
1261
|
}
|
|
660
|
-
const conn = this.peerInstance.connect(
|
|
1262
|
+
const conn = this.peerInstance.connect(nextHopId, { reliable: true });
|
|
1263
|
+
const startTime = Date.now();
|
|
661
1264
|
const timeout = setTimeout(() => {
|
|
662
1265
|
conn.close();
|
|
663
|
-
reject(new Error(`Relay timeout: ${
|
|
664
|
-
},
|
|
1266
|
+
reject(new Error(`Relay timeout: ${nextHopId}${path}`));
|
|
1267
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
665
1268
|
conn.on("open", () => {
|
|
666
|
-
this.debugLog("Conn", "open",
|
|
1269
|
+
this.debugLog("Conn", "open", nextHopId);
|
|
667
1270
|
const request = { path, data };
|
|
668
1271
|
const message = {
|
|
669
1272
|
type: "relay-request",
|
|
670
1273
|
id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,
|
|
671
1274
|
originalTarget: targetId,
|
|
672
|
-
relayPath: [],
|
|
673
|
-
forwardPath:
|
|
1275
|
+
relayPath: [this.myPeerId],
|
|
1276
|
+
forwardPath: [],
|
|
1277
|
+
ttl: DEFAULT_TTL,
|
|
674
1278
|
request
|
|
675
1279
|
};
|
|
676
1280
|
conn.send(message);
|
|
@@ -685,102 +1289,204 @@ var PeerJsWrapper = class {
|
|
|
685
1289
|
if (response.status < 200 || response.status >= 300) {
|
|
686
1290
|
reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));
|
|
687
1291
|
} else {
|
|
688
|
-
|
|
689
|
-
this.
|
|
1292
|
+
const latency = Date.now() - startTime;
|
|
1293
|
+
this.router.recordDirectNode(nextHopId, latency);
|
|
1294
|
+
this.router.broadcastRouteUpdate();
|
|
690
1295
|
resolve(response.data);
|
|
691
1296
|
}
|
|
692
1297
|
}
|
|
693
1298
|
} else if (message.type === "route-update") {
|
|
694
|
-
this.
|
|
1299
|
+
this.router.handleRouteUpdate(nextHopId, message);
|
|
1300
|
+
} else if (message.type === "route-query") {
|
|
1301
|
+
this.router.handleRouteQuery(nextHopId, message);
|
|
1302
|
+
} else if (message.type === "route-response") {
|
|
1303
|
+
this.router.handleRouteResponse(nextHopId, message);
|
|
695
1304
|
}
|
|
696
1305
|
});
|
|
697
1306
|
conn.on("error", (err) => {
|
|
698
|
-
this.debugLog("Conn", "error", { peer:
|
|
1307
|
+
this.debugLog("Conn", "error", { peer: nextHopId, error: err });
|
|
699
1308
|
clearTimeout(timeout);
|
|
700
1309
|
reject(err);
|
|
701
1310
|
});
|
|
702
1311
|
conn.on("close", () => {
|
|
703
|
-
this.debugLog("Conn", "close",
|
|
1312
|
+
this.debugLog("Conn", "close", nextHopId);
|
|
704
1313
|
clearTimeout(timeout);
|
|
705
1314
|
reject(new Error("Relay connection closed"));
|
|
706
1315
|
});
|
|
707
1316
|
}).catch(reject);
|
|
708
1317
|
});
|
|
709
1318
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
1319
|
+
/**
|
|
1320
|
+
* 自动路由发送
|
|
1321
|
+
*
|
|
1322
|
+
* 1. 查路由表 → 有路由 → 尝试中继 → 全部失败 → 降级直连 → 失败 → 结束
|
|
1323
|
+
* 2. 路由表无目标 → 直连 → 失败 → 结束
|
|
1324
|
+
* @param peerId 目标节点 ID
|
|
1325
|
+
* @param path 请求路径
|
|
1326
|
+
* @param data 请求数据
|
|
1327
|
+
* @returns Promise<unknown> - 响应数据
|
|
1328
|
+
*/
|
|
716
1329
|
send(peerId, path, data) {
|
|
1330
|
+
const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;
|
|
717
1331
|
return new Promise((resolve, reject) => {
|
|
718
1332
|
this.debugLog("PeerJsWrapper", "send", { peerId, path, data });
|
|
1333
|
+
const nextHops = this.router.getNextHopsToTarget(peerId);
|
|
1334
|
+
if (nextHops.length > 0) {
|
|
1335
|
+
this.tryRelayChain(peerId, path, data, nextHops, 0).then(resolve).catch((relayErr) => {
|
|
1336
|
+
this.debugLog("PeerJsWrapper", "relayFailed", { peerId, error: relayErr.message });
|
|
1337
|
+
this.fallbackToDirect(peerId, path, data, requestId).then(resolve).catch(reject);
|
|
1338
|
+
});
|
|
1339
|
+
} else {
|
|
1340
|
+
this.tryDirectConnect(peerId, path, data, requestId).then(resolve).catch((directErr) => {
|
|
1341
|
+
this.debugLog("PeerJsWrapper", "directFailed", { peerId, error: directErr.message });
|
|
1342
|
+
this.handleSendError(peerId, directErr).then(resolve).catch(reject);
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* 处理发送错误
|
|
1349
|
+
* @param peerId 目标节点 ID
|
|
1350
|
+
* @param error 错误对象
|
|
1351
|
+
* @returns Promise
|
|
1352
|
+
*/
|
|
1353
|
+
async handleSendError(peerId, error) {
|
|
1354
|
+
const errorMsg = error.message;
|
|
1355
|
+
const isHttpError = errorMsg.includes("Request failed:") || errorMsg.includes("404") || errorMsg.includes("500");
|
|
1356
|
+
const isConnectionError = errorMsg.includes("timeout") || errorMsg.includes("Connection closed") || errorMsg.includes("Peer instance not available");
|
|
1357
|
+
if (isHttpError) {
|
|
1358
|
+
throw error;
|
|
1359
|
+
}
|
|
1360
|
+
if (!isConnectionError) {
|
|
1361
|
+
throw error;
|
|
1362
|
+
}
|
|
1363
|
+
this.router.removeRoute(peerId);
|
|
1364
|
+
this.debugLog("PeerJsWrapper", "routeRemoved", peerId);
|
|
1365
|
+
if (!this.router.isRoutingTableEmpty()) {
|
|
1366
|
+
return this.performRouteDiscovery(peerId, "", void 0, "");
|
|
1367
|
+
}
|
|
1368
|
+
throw new Error(`Cannot reach ${peerId}: no route found and routing table is empty`);
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* 降级到直连尝试
|
|
1372
|
+
* @param peerId 目标节点 ID
|
|
1373
|
+
* @param path 请求路径
|
|
1374
|
+
* @param data 请求数据
|
|
1375
|
+
* @param requestId 请求 ID
|
|
1376
|
+
* @returns Promise
|
|
1377
|
+
*/
|
|
1378
|
+
async fallbackToDirect(peerId, path, data, requestId) {
|
|
1379
|
+
this.debugLog("PeerJsWrapper", "fallbackToDirect", peerId);
|
|
1380
|
+
return this.tryDirectConnect(peerId, path, data, requestId).catch((directErr) => {
|
|
1381
|
+
this.debugLog("PeerJsWrapper", "directFailed", { peerId, error: directErr.message });
|
|
1382
|
+
this.handleSendError(peerId, directErr);
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* 尝试通过中继链转发
|
|
1387
|
+
* @param targetId 目标节点 ID
|
|
1388
|
+
* @param path 请求路径
|
|
1389
|
+
* @param data 请求数据
|
|
1390
|
+
* @param nextHops 下一跳列表
|
|
1391
|
+
* @param index 当前尝试的下一跳索引
|
|
1392
|
+
* @returns Promise<unknown>
|
|
1393
|
+
*/
|
|
1394
|
+
tryRelayChain(targetId, path, data, nextHops, index) {
|
|
1395
|
+
if (index >= nextHops.length) {
|
|
1396
|
+
return Promise.reject(new Error("All relay nodes failed"));
|
|
1397
|
+
}
|
|
1398
|
+
const nextHop = nextHops[index];
|
|
1399
|
+
this.debugLog("PeerJsWrapper", "tryRelay", { targetId, nextHop: nextHop.nodeId, latency: nextHop.latency });
|
|
1400
|
+
return this.relayVia(nextHop.nodeId, targetId, path, data).catch(() => {
|
|
1401
|
+
return this.tryRelayChain(targetId, path, data, nextHops, index + 1);
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* 执行路由发现
|
|
1406
|
+
* @param targetId 目标节点 ID
|
|
1407
|
+
* @param path 请求路径
|
|
1408
|
+
* @param data 请求数据
|
|
1409
|
+
* @param requestId 请求 ID
|
|
1410
|
+
* @returns Promise<unknown>
|
|
1411
|
+
*/
|
|
1412
|
+
async performRouteDiscovery(targetId, path, data, requestId) {
|
|
1413
|
+
this.debugLog("PeerJsWrapper", "routeDiscovery", { targetId });
|
|
1414
|
+
const routeEntry = await this.router.discoverRoute(targetId);
|
|
1415
|
+
if (!routeEntry || routeEntry.nextHops.length === 0) {
|
|
1416
|
+
throw new Error(`Cannot reach ${targetId}: no route found`);
|
|
1417
|
+
}
|
|
1418
|
+
this.debugLog("PeerJsWrapper", "routeFound", { targetId, nextHops: routeEntry.nextHops });
|
|
1419
|
+
return this.tryRelayChain(targetId, path, data, routeEntry.nextHops, 0);
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* 中继发送(内部方法,不对外暴露)
|
|
1423
|
+
* @param targetId 目标节点 ID
|
|
1424
|
+
* @param path 请求路径
|
|
1425
|
+
* @param data 请求数据
|
|
1426
|
+
* @param relayNodes 手动指定的中继节点(可选,不指定则自动路由)
|
|
1427
|
+
* @returns Promise<unknown>
|
|
1428
|
+
*/
|
|
1429
|
+
relaySend(targetId, path, data, relayNodes) {
|
|
1430
|
+
if (!relayNodes || relayNodes.length === 0) {
|
|
1431
|
+
return this.send(targetId, path, data);
|
|
1432
|
+
}
|
|
1433
|
+
const [firstRelay, ...remainingRelays] = relayNodes;
|
|
1434
|
+
return new Promise((resolve, reject) => {
|
|
1435
|
+
this.debugLog("PeerJsWrapper", "relaySend", { targetId, firstRelay, remainingRelays });
|
|
719
1436
|
this.waitForReady().then(() => {
|
|
720
1437
|
if (!this.peerInstance) {
|
|
721
1438
|
reject(new Error("Peer instance not available"));
|
|
722
1439
|
return;
|
|
723
1440
|
}
|
|
724
|
-
const conn = this.peerInstance.connect(
|
|
1441
|
+
const conn = this.peerInstance.connect(firstRelay, { reliable: true });
|
|
725
1442
|
const timeout = setTimeout(() => {
|
|
726
1443
|
conn.close();
|
|
727
|
-
reject(new Error(`
|
|
728
|
-
},
|
|
729
|
-
const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;
|
|
730
|
-
this.pendingRequests.set(requestId, { resolve, reject, timeout });
|
|
1444
|
+
reject(new Error(`Relay timeout: ${firstRelay}${path}`));
|
|
1445
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
731
1446
|
conn.on("open", () => {
|
|
732
|
-
this.debugLog("Conn", "open",
|
|
1447
|
+
this.debugLog("Conn", "open", firstRelay);
|
|
733
1448
|
const request = { path, data };
|
|
734
1449
|
const message = {
|
|
735
|
-
type: "request",
|
|
736
|
-
id:
|
|
1450
|
+
type: "relay-request",
|
|
1451
|
+
id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,
|
|
1452
|
+
originalTarget: targetId,
|
|
1453
|
+
relayPath: [],
|
|
1454
|
+
forwardPath: remainingRelays,
|
|
1455
|
+
ttl: DEFAULT_TTL,
|
|
737
1456
|
request
|
|
738
1457
|
};
|
|
739
1458
|
conn.send(message);
|
|
740
1459
|
});
|
|
741
1460
|
conn.on("data", (responseData) => {
|
|
742
|
-
this.debugLog("Conn", "data", { peer: peerId, data: responseData });
|
|
743
1461
|
const message = responseData;
|
|
744
|
-
if (message.type === "response"
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const response = message.response;
|
|
1462
|
+
if (message.type === "relay-response") {
|
|
1463
|
+
clearTimeout(timeout);
|
|
1464
|
+
conn.close();
|
|
1465
|
+
const response = message.response;
|
|
1466
|
+
if (response) {
|
|
750
1467
|
if (response.status < 200 || response.status >= 300) {
|
|
751
|
-
|
|
752
|
-
new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`)
|
|
753
|
-
);
|
|
1468
|
+
reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));
|
|
754
1469
|
} else {
|
|
755
|
-
this.
|
|
756
|
-
this.
|
|
757
|
-
|
|
1470
|
+
this.router.recordSuccessfulNode(firstRelay);
|
|
1471
|
+
this.router.broadcastRouteUpdate();
|
|
1472
|
+
resolve(response.data);
|
|
758
1473
|
}
|
|
759
1474
|
}
|
|
760
|
-
|
|
1475
|
+
} else if (message.type === "route-update") {
|
|
1476
|
+
this.router.handleRouteUpdate(firstRelay, message);
|
|
761
1477
|
}
|
|
762
1478
|
});
|
|
763
1479
|
conn.on("error", (err) => {
|
|
764
|
-
this.debugLog("Conn", "error", { peer:
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
clearTimeout(pending.timeout);
|
|
768
|
-
this.pendingRequests.delete(requestId);
|
|
769
|
-
pending.reject(err);
|
|
770
|
-
}
|
|
1480
|
+
this.debugLog("Conn", "error", { peer: firstRelay, error: err });
|
|
1481
|
+
clearTimeout(timeout);
|
|
1482
|
+
reject(err);
|
|
771
1483
|
});
|
|
772
1484
|
conn.on("close", () => {
|
|
773
|
-
this.debugLog("Conn", "close",
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
clearTimeout(pending.timeout);
|
|
777
|
-
this.pendingRequests.delete(requestId);
|
|
778
|
-
pending.reject(new Error("Connection closed"));
|
|
779
|
-
}
|
|
1485
|
+
this.debugLog("Conn", "close", firstRelay);
|
|
1486
|
+
clearTimeout(timeout);
|
|
1487
|
+
reject(new Error("Relay connection closed"));
|
|
780
1488
|
});
|
|
781
|
-
}).catch(
|
|
782
|
-
reject(err);
|
|
783
|
-
});
|
|
1489
|
+
}).catch(reject);
|
|
784
1490
|
});
|
|
785
1491
|
}
|
|
786
1492
|
setupIncomingConnectionHandler() {
|
|
@@ -847,7 +1553,7 @@ var PeerJsWrapper = class {
|
|
|
847
1553
|
}
|
|
848
1554
|
}
|
|
849
1555
|
} else if (message.type === "route-update") {
|
|
850
|
-
this.
|
|
1556
|
+
this.router.handleRouteUpdate(conn.peer, message);
|
|
851
1557
|
}
|
|
852
1558
|
});
|
|
853
1559
|
conn.on("close", () => {
|
|
@@ -1035,10 +1741,23 @@ var PeerJsWrapper = class {
|
|
|
1035
1741
|
this.peerInstance.destroy();
|
|
1036
1742
|
this.peerInstance = null;
|
|
1037
1743
|
}
|
|
1744
|
+
if (this.router) {
|
|
1745
|
+
this.router.persist();
|
|
1746
|
+
this.router.destroy();
|
|
1747
|
+
}
|
|
1038
1748
|
}
|
|
1039
1749
|
};
|
|
1040
1750
|
export {
|
|
1041
1751
|
PeerJsWrapper,
|
|
1042
|
-
VERSION
|
|
1752
|
+
VERSION,
|
|
1753
|
+
clearAllRoutingData,
|
|
1754
|
+
deleteRouteEntry,
|
|
1755
|
+
initRoutingDB,
|
|
1756
|
+
loadDirectNodes,
|
|
1757
|
+
loadRoutingTable,
|
|
1758
|
+
saveDirectNode,
|
|
1759
|
+
saveDirectNodes,
|
|
1760
|
+
saveRouteEntries,
|
|
1761
|
+
saveRouteEntry
|
|
1043
1762
|
};
|
|
1044
1763
|
//# sourceMappingURL=index.esm.js.map
|