sx-peerjs-http-util 1.0.6 → 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/dist/index.umd.js CHANGED
@@ -647,7 +647,16 @@ var PeerJsHttpUtil = (() => {
647
647
  var index_exports = {};
648
648
  __export(index_exports, {
649
649
  PeerJsWrapper: () => PeerJsWrapper,
650
- VERSION: () => VERSION
650
+ VERSION: () => VERSION,
651
+ clearAllRoutingData: () => clearAllRoutingData,
652
+ deleteRouteEntry: () => deleteRouteEntry,
653
+ initRoutingDB: () => initRoutingDB,
654
+ loadDirectNodes: () => loadDirectNodes,
655
+ loadRoutingTable: () => loadRoutingTable,
656
+ saveDirectNode: () => saveDirectNode,
657
+ saveDirectNodes: () => saveDirectNodes,
658
+ saveRouteEntries: () => saveRouteEntries,
659
+ saveRouteEntry: () => saveRouteEntry
651
660
  });
652
661
 
653
662
  // node_modules/peerjs-js-binarypack/dist/binarypack.mjs
@@ -4653,24 +4662,38 @@ var PeerJsHttpUtil = (() => {
4653
4662
  }
4654
4663
  };
4655
4664
 
4656
- // src/index.ts
4657
- var VERSION = "1.0.6";
4658
- console.log(`[sx-peerjs-http-util] v${VERSION}`);
4659
- function generateUUID() {
4660
- return crypto.randomUUID();
4661
- }
4665
+ // src/CallSession.ts
4662
4666
  var CallSessionImpl = class {
4667
+ /** 对端的 Peer ID */
4663
4668
  peerId;
4669
+ /** 是否包含视频 */
4664
4670
  hasVideo;
4671
+ /** PeerJS MediaConnection 实例 */
4665
4672
  mediaConnection;
4673
+ /** 本地媒体流(麦克风/摄像头) */
4666
4674
  localStream = null;
4675
+ /** 远程媒体流(对方的音频/视频) */
4667
4676
  remoteStream = null;
4677
+ /** 通话状态监听器集合 */
4668
4678
  stateListeners = /* @__PURE__ */ new Set();
4679
+ /** 调试日志函数 */
4669
4680
  debugLogFn;
4681
+ /** 清理回调(通话结束时调用) */
4670
4682
  onCleanup;
4683
+ /** 当前通话状态 */
4671
4684
  _state = "connecting";
4685
+ /** 是否已静音 */
4672
4686
  isMuted = false;
4687
+ /** 视频是否开启 */
4673
4688
  isVideoEnabled = true;
4689
+ /**
4690
+ * 创建通话会话实例
4691
+ * @param peerId 对端 Peer ID
4692
+ * @param mediaConnection PeerJS MediaConnection 实例
4693
+ * @param hasVideo 是否包含视频
4694
+ * @param debugLog 调试日志函数
4695
+ * @param onCleanup 通话结束时的清理回调
4696
+ */
4674
4697
  constructor(peerId, mediaConnection, hasVideo, debugLog, onCleanup) {
4675
4698
  this.peerId = peerId;
4676
4699
  this.mediaConnection = mediaConnection;
@@ -4678,28 +4701,43 @@ var PeerJsHttpUtil = (() => {
4678
4701
  this.debugLogFn = debugLog;
4679
4702
  this.onCleanup = onCleanup;
4680
4703
  }
4704
+ /** 是否已连接 */
4681
4705
  get isConnected() {
4682
4706
  return this._state === "connected";
4683
4707
  }
4708
+ /** 当前通话状态 */
4684
4709
  get state() {
4685
4710
  return this._state;
4686
4711
  }
4712
+ /**
4713
+ * 设置通话状态
4714
+ * @param state 新的通话状态
4715
+ * @param reason 状态变化原因(可选)
4716
+ */
4687
4717
  setState(state, reason) {
4688
4718
  this._state = state;
4689
4719
  this.notifyStateChange(state, reason);
4690
4720
  }
4721
+ /** 设置本地媒体流 */
4691
4722
  setLocalStream(stream) {
4692
4723
  this.localStream = stream;
4693
4724
  }
4725
+ /** 设置远程媒体流 */
4694
4726
  setRemoteStream(stream) {
4695
4727
  this.remoteStream = stream;
4696
4728
  }
4729
+ /** 获取本地媒体流 */
4697
4730
  getLocalStream() {
4698
4731
  return this.localStream;
4699
4732
  }
4733
+ /** 获取远程媒体流 */
4700
4734
  getRemoteStream() {
4701
4735
  return this.remoteStream;
4702
4736
  }
4737
+ /**
4738
+ * 切换静音状态
4739
+ * @returns 切换后的静音状态(true = 已静音)
4740
+ */
4703
4741
  toggleMute() {
4704
4742
  if (!this.localStream) return this.isMuted;
4705
4743
  const audioTracks = this.localStream.getAudioTracks();
@@ -4710,6 +4748,10 @@ var PeerJsHttpUtil = (() => {
4710
4748
  this.debugLogFn("CallSession", "toggleMute", this.isMuted);
4711
4749
  return this.isMuted;
4712
4750
  }
4751
+ /**
4752
+ * 切换视频开关(仅视频通话有效)
4753
+ * @returns 切换后的视频状态(true = 视频开启)
4754
+ */
4713
4755
  toggleVideo() {
4714
4756
  if (!this.hasVideo || !this.localStream) return this.isVideoEnabled;
4715
4757
  const videoTracks = this.localStream.getVideoTracks();
@@ -4720,10 +4762,15 @@ var PeerJsHttpUtil = (() => {
4720
4762
  this.debugLogFn("CallSession", "toggleVideo", this.isVideoEnabled);
4721
4763
  return this.isVideoEnabled;
4722
4764
  }
4765
+ /** 挂断通话 */
4723
4766
  hangUp() {
4724
4767
  this.debugLogFn("CallSession", "hangUp", this.peerId);
4725
4768
  this.mediaConnection.close();
4726
4769
  }
4770
+ /**
4771
+ * 关闭通话会话
4772
+ * 停止本地流,清理资源
4773
+ */
4727
4774
  close() {
4728
4775
  if (this.localStream) {
4729
4776
  this.localStream.getTracks().forEach((track) => track.stop());
@@ -4732,12 +4779,19 @@ var PeerJsHttpUtil = (() => {
4732
4779
  this.remoteStream = null;
4733
4780
  this._state = "ended";
4734
4781
  }
4782
+ /** 注册通话状态变化监听器 */
4735
4783
  onStateChange(listener) {
4736
4784
  this.stateListeners.add(listener);
4737
4785
  }
4786
+ /** 移除通话状态变化监听器 */
4738
4787
  offStateChange(listener) {
4739
4788
  this.stateListeners.delete(listener);
4740
4789
  }
4790
+ /**
4791
+ * 通知状态变化
4792
+ * @param state 新状态
4793
+ * @param reason 变化原因(可选)
4794
+ */
4741
4795
  notifyStateChange(state, reason) {
4742
4796
  this.debugLogFn("CallSession", "stateChange", { peer: this.peerId, state, reason });
4743
4797
  this.stateListeners.forEach((listener) => {
@@ -4752,86 +4806,916 @@ var PeerJsHttpUtil = (() => {
4752
4806
  }
4753
4807
  }
4754
4808
  };
4755
- var PeerJsWrapper = class {
4809
+
4810
+ // src/constants.ts
4811
+ var CONNECTION_TIMEOUT_MS = 3e4;
4812
+ var SEND_TIMEOUT_MS = 1e4;
4813
+ var RECONNECT_DELAY_MS = 1e3;
4814
+ var ROUTE_EXPIRE_AGE_MS = 5 * 60 * 1e3;
4815
+ var MAX_DIRECT_NODES = 5;
4816
+ var ROUTE_CLEANUP_INTERVAL_MS = 60 * 1e3;
4817
+ var ROUTE_BROADCAST_INTERVAL_MS = 30 * 1e3;
4818
+ var DEFAULT_TTL = 128;
4819
+
4820
+ // src/RoutingDB.ts
4821
+ var DB_NAME = "peerjs-routing-db";
4822
+ var DB_VERSION = 1;
4823
+ var ROUTING_TABLE_STORE = "routing-table";
4824
+ var DIRECT_NODES_STORE = "direct-nodes";
4825
+ var db = null;
4826
+ function withStore(storeName, mode, operation) {
4827
+ return openDB().then((database) => {
4828
+ return new Promise((resolve, reject) => {
4829
+ const tx = database.transaction(storeName, mode);
4830
+ const store = tx.objectStore(storeName);
4831
+ const request = operation(store);
4832
+ if (request) {
4833
+ request.onerror = () => reject(request.error);
4834
+ request.onsuccess = () => resolve(request.result);
4835
+ } else {
4836
+ tx.oncomplete = () => resolve(void 0);
4837
+ tx.onerror = () => reject(tx.error);
4838
+ }
4839
+ });
4840
+ });
4841
+ }
4842
+ function openDB() {
4843
+ return new Promise((resolve, reject) => {
4844
+ if (db) {
4845
+ resolve(db);
4846
+ return;
4847
+ }
4848
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
4849
+ request.onerror = () => reject(request.error);
4850
+ request.onsuccess = () => {
4851
+ db = request.result;
4852
+ resolve(db);
4853
+ };
4854
+ request.onupgradeneeded = (e) => {
4855
+ const req = e.target;
4856
+ const database = req.result;
4857
+ if (!database.objectStoreNames.contains(ROUTING_TABLE_STORE)) {
4858
+ const routingStore = database.createObjectStore(ROUTING_TABLE_STORE, {
4859
+ keyPath: "target"
4860
+ });
4861
+ routingStore.createIndex("timestamp", "timestamp", { unique: false });
4862
+ }
4863
+ if (!database.objectStoreNames.contains(DIRECT_NODES_STORE)) {
4864
+ const nodesStore = database.createObjectStore(DIRECT_NODES_STORE, {
4865
+ keyPath: "nodeId"
4866
+ });
4867
+ nodesStore.createIndex("timestamp", "timestamp", { unique: false });
4868
+ }
4869
+ };
4870
+ });
4871
+ }
4872
+ async function initRoutingDB() {
4873
+ await openDB();
4874
+ }
4875
+ async function saveRouteEntry(entry) {
4876
+ await withStore(ROUTING_TABLE_STORE, "readwrite", (store) => store.put(entry));
4877
+ }
4878
+ async function saveRouteEntries(entries) {
4879
+ await openDB().then((database) => {
4880
+ return new Promise((resolve, reject) => {
4881
+ const tx = database.transaction(ROUTING_TABLE_STORE, "readwrite");
4882
+ const store = tx.objectStore(ROUTING_TABLE_STORE);
4883
+ for (const entry of entries) {
4884
+ store.put(entry);
4885
+ }
4886
+ tx.oncomplete = () => resolve();
4887
+ tx.onerror = () => reject(tx.error);
4888
+ });
4889
+ });
4890
+ }
4891
+ async function deleteRouteEntry(target) {
4892
+ await withStore(ROUTING_TABLE_STORE, "readwrite", (store) => store.delete(target));
4893
+ }
4894
+ async function loadRoutingTable() {
4895
+ return withStore(ROUTING_TABLE_STORE, "readonly", (store) => store.getAll());
4896
+ }
4897
+ async function saveDirectNode(node) {
4898
+ await withStore(DIRECT_NODES_STORE, "readwrite", (store) => store.put(node));
4899
+ }
4900
+ async function saveDirectNodes(nodes) {
4901
+ await openDB().then((database) => {
4902
+ return new Promise((resolve, reject) => {
4903
+ const tx = database.transaction(DIRECT_NODES_STORE, "readwrite");
4904
+ const store = tx.objectStore(DIRECT_NODES_STORE);
4905
+ for (const node of nodes) {
4906
+ store.put(node);
4907
+ }
4908
+ tx.oncomplete = () => resolve();
4909
+ tx.onerror = () => reject(tx.error);
4910
+ });
4911
+ });
4912
+ }
4913
+ async function loadDirectNodes() {
4914
+ return withStore(DIRECT_NODES_STORE, "readonly", (store) => store.getAll());
4915
+ }
4916
+ async function clearAllRoutingData() {
4917
+ const database = await openDB();
4918
+ await new Promise((resolve, reject) => {
4919
+ const tx = database.transaction(ROUTING_TABLE_STORE, "readwrite");
4920
+ const store = tx.objectStore(ROUTING_TABLE_STORE);
4921
+ store.clear();
4922
+ tx.oncomplete = () => resolve();
4923
+ tx.onerror = () => reject(tx.error);
4924
+ });
4925
+ await new Promise((resolve, reject) => {
4926
+ const tx = database.transaction(DIRECT_NODES_STORE, "readwrite");
4927
+ const store = tx.objectStore(DIRECT_NODES_STORE);
4928
+ store.clear();
4929
+ tx.oncomplete = () => resolve();
4930
+ tx.onerror = () => reject(tx.error);
4931
+ });
4932
+ }
4933
+
4934
+ // src/Router.ts
4935
+ var Router = class {
4936
+ /** 路由表:target -> RouteEntry */
4937
+ routingTable = /* @__PURE__ */ new Map();
4938
+ /** 直连节点及延迟列表 */
4939
+ directNodes = [];
4940
+ /** 中继配置 */
4941
+ relayConfig;
4942
+ /** 回调函数集合 */
4943
+ callbacks;
4944
+ /** 等待路由发现响应的 pending 队列 */
4945
+ pendingRouteQueries = /* @__PURE__ */ new Map();
4946
+ /** 定时清理定时器 */
4947
+ cleanupTimer = null;
4948
+ /** 周期广播定时器 */
4949
+ broadcastTimer = null;
4756
4950
  /**
4757
- * 本地 Peer ID,构造时确定(传入或自动生成)
4951
+ * 创建路由管理器
4952
+ * @param callbacks 回调函数集合
4953
+ * @param relayConfig 中继配置(可选)
4758
4954
  */
4759
- myPeerId;
4955
+ constructor(callbacks, relayConfig) {
4956
+ this.callbacks = callbacks;
4957
+ this.relayConfig = relayConfig ?? {};
4958
+ }
4760
4959
  /**
4761
- * PeerJS 实例
4960
+ * 初始化路由管理器(从 IndexedDB 加载数据并启动定时任务)
4762
4961
  */
4763
- peerInstance = null;
4962
+ async init() {
4963
+ await initRoutingDB();
4964
+ await this.loadFromDB();
4965
+ this.startMaintenanceTasks();
4966
+ }
4764
4967
  /**
4765
- * 当前活跃的传入连接集合
4968
+ * 从 IndexedDB 加载路由数据
4766
4969
  */
4767
- connections = /* @__PURE__ */ new Set();
4970
+ async loadFromDB() {
4971
+ try {
4972
+ const routes = await loadRoutingTable();
4973
+ for (const entry of routes) {
4974
+ this.routingTable.set(entry.target, entry);
4975
+ }
4976
+ this.callbacks.debugLog("Routing", "loadedRoutes", routes.length);
4977
+ } catch (e) {
4978
+ this.callbacks.debugLog("Routing", "loadError", e);
4979
+ }
4980
+ try {
4981
+ const nodes = await loadDirectNodes();
4982
+ this.directNodes = nodes;
4983
+ this.callbacks.debugLog("Routing", "loadedNodes", nodes.length);
4984
+ } catch (e) {
4985
+ this.callbacks.debugLog("Routing", "loadNodesError", e);
4986
+ }
4987
+ }
4768
4988
  /**
4769
- * 待处理的请求映射表(用于请求-响应匹配)
4989
+ * 启动定时维护任务
4770
4990
  */
4771
- pendingRequests = /* @__PURE__ */ new Map();
4991
+ startMaintenanceTasks() {
4992
+ this.cleanupTimer = setInterval(() => {
4993
+ this.cleanupExpiredEntries();
4994
+ }, ROUTE_CLEANUP_INTERVAL_MS);
4995
+ this.broadcastTimer = setInterval(() => {
4996
+ this.broadcastRouteUpdate();
4997
+ }, ROUTE_BROADCAST_INTERVAL_MS);
4998
+ }
4772
4999
  /**
4773
- * 路径处理器映射表
5000
+ * 清理过期路由条目
4774
5001
  */
4775
- simpleHandlers = /* @__PURE__ */ new Map();
5002
+ async cleanupExpiredEntries() {
5003
+ const now = Date.now();
5004
+ for (const [target, entry] of this.routingTable) {
5005
+ if (now - entry.timestamp > ROUTE_EXPIRE_AGE_MS) {
5006
+ this.routingTable.delete(target);
5007
+ deleteRouteEntry(target).catch(() => {
5008
+ });
5009
+ }
5010
+ }
5011
+ this.directNodes = this.directNodes.filter((node) => {
5012
+ const isExpired = now - node.timestamp > ROUTE_EXPIRE_AGE_MS;
5013
+ if (isExpired) {
5014
+ return false;
5015
+ }
5016
+ return true;
5017
+ });
5018
+ this.callbacks.debugLog("Routing", "cleanup", {
5019
+ routes: this.routingTable.size,
5020
+ nodes: this.directNodes.length
5021
+ });
5022
+ }
4776
5023
  /**
4777
- * 重连定时器
5024
+ * 持久化路由表到 IndexedDB
4778
5025
  */
4779
- reconnectTimer = null;
5026
+ async persist() {
5027
+ try {
5028
+ const entries = Array.from(this.routingTable.values());
5029
+ await saveRouteEntries(entries);
5030
+ } catch (e) {
5031
+ this.callbacks.debugLog("Routing", "persistError", e);
5032
+ }
5033
+ try {
5034
+ await saveDirectNodes(this.directNodes);
5035
+ } catch (e) {
5036
+ this.callbacks.debugLog("Routing", "persistNodesError", e);
5037
+ }
5038
+ }
4780
5039
  /**
4781
- * 是否已销毁
5040
+ * 销毁路由管理器(清理定时器)
4782
5041
  */
4783
- isDestroyed = false;
5042
+ destroy() {
5043
+ if (this.cleanupTimer) {
5044
+ clearInterval(this.cleanupTimer);
5045
+ this.cleanupTimer = null;
5046
+ }
5047
+ if (this.broadcastTimer) {
5048
+ clearInterval(this.broadcastTimer);
5049
+ this.broadcastTimer = null;
5050
+ }
5051
+ this.pendingRouteQueries.forEach((pending) => {
5052
+ clearTimeout(pending.timer);
5053
+ });
5054
+ this.pendingRouteQueries.clear();
5055
+ }
4784
5056
  /**
4785
- * 是否开启调试模式
5057
+ * 记录成功的直连通信
5058
+ * @param nodeId 节点 ID
5059
+ * @param latency 延迟(毫秒)
4786
5060
  */
4787
- isDebug;
5061
+ recordDirectNode(nodeId, latency) {
5062
+ const myPeerId = this.callbacks.getMyPeerId();
5063
+ if (nodeId === myPeerId) return;
5064
+ const existing = this.directNodes.find((n) => n.nodeId === nodeId);
5065
+ const timestamp = Date.now();
5066
+ if (existing) {
5067
+ existing.latency = latency;
5068
+ existing.timestamp = timestamp;
5069
+ } else {
5070
+ const maxRelayNodes = this.relayConfig.maxRelayNodes ?? MAX_DIRECT_NODES;
5071
+ this.directNodes.push({ nodeId, latency, timestamp });
5072
+ if (this.directNodes.length > maxRelayNodes) {
5073
+ this.directNodes.sort((a, b) => a.latency - b.latency);
5074
+ this.directNodes.shift();
5075
+ }
5076
+ }
5077
+ this.callbacks.debugLog("Routing", "directNode", { nodeId, latency });
5078
+ }
4788
5079
  /**
4789
- * 服务器配置
5080
+ * 获取直连节点列表(按延迟升序)
5081
+ * @returns 直连节点列表
4790
5082
  */
4791
- serverConfig;
5083
+ getDirectNodes() {
5084
+ return [...this.directNodes].sort((a, b) => a.latency - b.latency);
5085
+ }
4792
5086
  /**
4793
- * 当前活跃的通话
5087
+ * 检查是否可以直连目标节点
5088
+ * @param targetId 目标节点 ID
5089
+ * @returns 是否可以直连
4794
5090
  */
4795
- activeCall = null;
5091
+ canReachDirectly(targetId) {
5092
+ return this.directNodes.some((n) => n.nodeId === targetId);
5093
+ }
5094
+ /**
5095
+ * 获取到直连节点的延迟
5096
+ * @param nodeId 节点 ID
5097
+ * @returns 延迟(毫秒),如果不存在返回 null
5098
+ */
5099
+ getDirectLatency(nodeId) {
5100
+ const node = this.directNodes.find((n) => n.nodeId === nodeId);
5101
+ return node ? node.latency : null;
5102
+ }
5103
+ /**
5104
+ * 移除失效的路由(通信失败时调用)
5105
+ * @param nodeId 失效的节点 ID
5106
+ */
5107
+ removeRoute(nodeId) {
5108
+ let removed = false;
5109
+ for (const [target, entry] of this.routingTable) {
5110
+ const originalLength = entry.nextHops.length;
5111
+ entry.nextHops = entry.nextHops.filter((h) => h.nodeId !== nodeId);
5112
+ if (entry.nextHops.length === 0) {
5113
+ this.routingTable.delete(target);
5114
+ deleteRouteEntry(target).catch(() => {
5115
+ });
5116
+ removed = true;
5117
+ } else if (entry.nextHops.length < originalLength) {
5118
+ entry.timestamp = Date.now();
5119
+ saveRouteEntry(entry).catch(() => {
5120
+ });
5121
+ removed = true;
5122
+ }
5123
+ }
5124
+ const nodeIndex = this.directNodes.findIndex((n) => n.nodeId === nodeId);
5125
+ if (nodeIndex !== -1) {
5126
+ this.directNodes.splice(nodeIndex, 1);
5127
+ removed = true;
5128
+ }
5129
+ if (removed) {
5130
+ this.callbacks.debugLog("Routing", "routeRemoved", nodeId);
5131
+ }
5132
+ }
5133
+ /**
5134
+ * 检查路由表是否为空
5135
+ * @returns 是否为空
5136
+ */
5137
+ isRoutingTableEmpty() {
5138
+ return this.routingTable.size === 0;
5139
+ }
5140
+ /**
5141
+ * 记录成功通信的节点(兼容旧接口)
5142
+ * @param nodeId 节点 ID
5143
+ */
5144
+ recordSuccessfulNode(nodeId) {
5145
+ const myPeerId = this.callbacks.getMyPeerId();
5146
+ if (nodeId === myPeerId) return;
5147
+ const existing = this.directNodes.find((n) => n.nodeId === nodeId);
5148
+ if (!existing) {
5149
+ const maxRelayNodes = this.relayConfig.maxRelayNodes ?? 5;
5150
+ this.directNodes.push({ nodeId, latency: 100, timestamp: Date.now() });
5151
+ if (this.directNodes.length > maxRelayNodes) {
5152
+ this.directNodes.sort((a, b) => a.latency - b.latency);
5153
+ this.directNodes.shift();
5154
+ }
5155
+ }
5156
+ }
5157
+ /**
5158
+ * 广播路由更新
5159
+ * 向所有直连节点发送路由更新消息,告知它们本节点可达的节点列表
5160
+ */
5161
+ async broadcastRouteUpdate() {
5162
+ const myPeerId = this.callbacks.getMyPeerId();
5163
+ const reachableNodes = this.getReachableNodes();
5164
+ for (const node of this.directNodes) {
5165
+ try {
5166
+ await this.sendRouteUpdate(node.nodeId, reachableNodes);
5167
+ } catch {
5168
+ }
5169
+ }
5170
+ }
5171
+ /**
5172
+ * 获取本节点可达的节点列表
5173
+ * @returns 可达节点数组(直连节点 + 自己)
5174
+ */
5175
+ getReachableNodes() {
5176
+ const myPeerId = this.callbacks.getMyPeerId();
5177
+ const directNodeIds = this.directNodes.map((n) => n.nodeId);
5178
+ return [.../* @__PURE__ */ new Set([...directNodeIds, myPeerId])];
5179
+ }
5180
+ /**
5181
+ * 发送路由更新到指定节点
5182
+ * @param targetId 目标节点 ID
5183
+ * @param reachableNodes 可达的节点列表
5184
+ */
5185
+ async sendRouteUpdate(targetId, reachableNodes) {
5186
+ const myPeerId = this.callbacks.getMyPeerId();
5187
+ const message = {
5188
+ type: "route-update",
5189
+ id: `${myPeerId}-route-${Date.now()}`,
5190
+ originalTarget: targetId,
5191
+ relayPath: [],
5192
+ forwardPath: [],
5193
+ ttl: DEFAULT_TTL,
5194
+ routeUpdate: { reachableNodes }
5195
+ };
5196
+ await this.callbacks.sendRelayMessage(targetId, message);
5197
+ }
5198
+ /**
5199
+ * 处理收到的路由更新
5200
+ * 合并对端发来的可达节点信息到本地路由表
5201
+ * @param fromPeerId 发送路由更新的节点 ID
5202
+ * @param message 路由更新消息
5203
+ */
5204
+ handleRouteUpdate(fromPeerId, message) {
5205
+ if (!message.routeUpdate) return;
5206
+ const myPeerId = this.callbacks.getMyPeerId();
5207
+ const { reachableNodes } = message.routeUpdate;
5208
+ const timestamp = Date.now();
5209
+ const viaLatency = this.getDirectLatency(fromPeerId) ?? 100;
5210
+ for (const target of reachableNodes) {
5211
+ if (target === myPeerId) continue;
5212
+ let entry = this.routingTable.get(target);
5213
+ const totalLatency = viaLatency + 100;
5214
+ if (!entry) {
5215
+ entry = {
5216
+ target,
5217
+ nextHops: [],
5218
+ hops: 1,
5219
+ timestamp
5220
+ };
5221
+ this.routingTable.set(target, entry);
5222
+ }
5223
+ const existingHop = entry.nextHops.find((h) => h.nodeId === fromPeerId);
5224
+ if (existingHop) {
5225
+ existingHop.latency = totalLatency;
5226
+ } else {
5227
+ entry.nextHops.push({ nodeId: fromPeerId, latency: totalLatency });
5228
+ }
5229
+ entry.nextHops.sort((a, b) => a.latency - b.latency);
5230
+ entry.hops = Math.min(entry.hops, 1);
5231
+ entry.timestamp = timestamp;
5232
+ this.callbacks.debugLog("Routing", "update", { target, nextHop: fromPeerId, latency: totalLatency });
5233
+ }
5234
+ }
5235
+ /**
5236
+ * 执行路由发现广播
5237
+ * 当直连和路由表都失败时,向所有直连节点广播询问谁能连通目标
5238
+ * @param targetId 目标节点 ID
5239
+ * @returns 路由条目(如果发现)
5240
+ */
5241
+ async discoverRoute(targetId) {
5242
+ const myPeerId = this.callbacks.getMyPeerId();
5243
+ const directNodes = this.getDirectNodes();
5244
+ if (directNodes.length === 0) {
5245
+ this.callbacks.debugLog("Routing", "discoverRoute", "no direct nodes");
5246
+ return null;
5247
+ }
5248
+ this.callbacks.debugLog("Routing", "discoverRoute", { targetId, directNodes: directNodes.length });
5249
+ const queryId = `${myPeerId}-query-${Date.now()}`;
5250
+ const routeEntry = await new Promise((resolve, reject) => {
5251
+ const timer = setTimeout(() => {
5252
+ this.pendingRouteQueries.delete(queryId);
5253
+ resolve(null);
5254
+ }, CONNECTION_TIMEOUT_MS);
5255
+ this.pendingRouteQueries.set(queryId, { resolve, reject, timer });
5256
+ const message = {
5257
+ type: "route-query",
5258
+ id: queryId,
5259
+ originalTarget: targetId,
5260
+ relayPath: [myPeerId],
5261
+ forwardPath: [],
5262
+ ttl: DEFAULT_TTL,
5263
+ routeQuery: {
5264
+ queryOrigin: myPeerId,
5265
+ targetNode: targetId,
5266
+ queryPath: [myPeerId]
5267
+ }
5268
+ };
5269
+ for (const node of directNodes) {
5270
+ this.callbacks.sendRelayMessage(node.nodeId, message).catch(() => {
5271
+ });
5272
+ }
5273
+ });
5274
+ return routeEntry;
5275
+ }
5276
+ /**
5277
+ * 处理路由查询消息
5278
+ * @param fromPeerId 发送查询的节点
5279
+ * @param message 路由查询消息
5280
+ */
5281
+ handleRouteQuery(fromPeerId, message) {
5282
+ if (!message.routeQuery) return;
5283
+ const myPeerId = this.callbacks.getMyPeerId();
5284
+ const { queryOrigin, targetNode, queryPath } = message.routeQuery;
5285
+ if (targetNode === myPeerId) {
5286
+ const latency = this.getDirectLatency(fromPeerId) ?? 100;
5287
+ const response = {
5288
+ type: "route-response",
5289
+ id: `${myPeerId}-resp-${Date.now()}`,
5290
+ originalTarget: queryOrigin,
5291
+ relayPath: [],
5292
+ forwardPath: [],
5293
+ ttl: DEFAULT_TTL,
5294
+ routeResponse: {
5295
+ queryOrigin,
5296
+ responder: myPeerId,
5297
+ targetNode,
5298
+ latency
5299
+ }
5300
+ };
5301
+ this.callbacks.sendRelayMessage(fromPeerId, response);
5302
+ return;
5303
+ }
5304
+ if (queryPath.includes(myPeerId)) {
5305
+ return;
5306
+ }
5307
+ const currentTTL = message.ttl ?? DEFAULT_TTL;
5308
+ if (currentTTL <= 0) {
5309
+ this.callbacks.debugLog("Routing", "ttlExpired", { type: "route-query", id: message.id });
5310
+ return;
5311
+ }
5312
+ const nextHop = this.findNextHopToTarget(targetNode);
5313
+ if (nextHop) {
5314
+ const latency = (this.getDirectLatency(fromPeerId) ?? 100) + nextHop.latency;
5315
+ const response = {
5316
+ type: "route-response",
5317
+ id: `${myPeerId}-resp-${Date.now()}`,
5318
+ originalTarget: queryOrigin,
5319
+ relayPath: [],
5320
+ forwardPath: [],
5321
+ ttl: DEFAULT_TTL,
5322
+ routeResponse: {
5323
+ queryOrigin,
5324
+ responder: myPeerId,
5325
+ targetNode,
5326
+ latency
5327
+ }
5328
+ };
5329
+ this.callbacks.sendRelayMessage(fromPeerId, response);
5330
+ return;
5331
+ }
5332
+ const newPath = [...queryPath, myPeerId];
5333
+ const forwardMessage = {
5334
+ ...message,
5335
+ relayPath: newPath,
5336
+ ttl: currentTTL - 1,
5337
+ routeQuery: {
5338
+ ...message.routeQuery,
5339
+ queryPath: newPath
5340
+ }
5341
+ };
5342
+ for (const node of this.directNodes) {
5343
+ if (node.nodeId !== fromPeerId) {
5344
+ this.callbacks.sendRelayMessage(node.nodeId, forwardMessage).catch(() => {
5345
+ });
5346
+ }
5347
+ }
5348
+ }
5349
+ /**
5350
+ * 处理路由查询响应
5351
+ * @param fromPeerId 响应者节点
5352
+ * @param message 路由响应消息
5353
+ */
5354
+ handleRouteResponse(fromPeerId, message) {
5355
+ if (!message.routeResponse) return;
5356
+ const { queryOrigin, targetNode, latency } = message.routeResponse;
5357
+ const myPeerId = this.callbacks.getMyPeerId();
5358
+ if (queryOrigin !== myPeerId) return;
5359
+ const pending = Array.from(this.pendingRouteQueries.values())[0];
5360
+ if (!pending) return;
5361
+ let entry = this.routingTable.get(targetNode);
5362
+ const timestamp = Date.now();
5363
+ if (!entry) {
5364
+ entry = {
5365
+ target: targetNode,
5366
+ nextHops: [],
5367
+ hops: 1,
5368
+ timestamp
5369
+ };
5370
+ this.routingTable.set(targetNode, entry);
5371
+ }
5372
+ const existingHop = entry.nextHops.find((h) => h.nodeId === fromPeerId);
5373
+ if (existingHop) {
5374
+ existingHop.latency = latency;
5375
+ } else {
5376
+ entry.nextHops.push({ nodeId: fromPeerId, latency });
5377
+ }
5378
+ entry.nextHops.sort((a, b) => a.latency - b.latency);
5379
+ entry.timestamp = timestamp;
5380
+ this.callbacks.debugLog("Routing", "discovered", { targetNode, nextHop: fromPeerId, latency });
5381
+ clearTimeout(pending.timer);
5382
+ pending.resolve(entry);
5383
+ }
5384
+ /**
5385
+ * 查找到目标节点的下一跳
5386
+ * @param targetId 目标节点 ID
5387
+ * @returns 下一跳信息,如果没有则返回 null
5388
+ */
5389
+ findNextHopToTarget(targetId) {
5390
+ const entry = this.routingTable.get(targetId);
5391
+ if (!entry || entry.nextHops.length === 0) return null;
5392
+ return entry.nextHops[0];
5393
+ }
5394
+ /**
5395
+ * 获取到目标节点的所有下一跳(按延迟升序)
5396
+ * @param targetId 目标节点 ID
5397
+ * @returns 下一跳列表
5398
+ */
5399
+ getNextHopsToTarget(targetId) {
5400
+ const entry = this.routingTable.get(targetId);
5401
+ return entry ? [...entry.nextHops] : [];
5402
+ }
5403
+ /**
5404
+ * 获取路由表
5405
+ * @returns 路由表对象
5406
+ */
5407
+ getRoutingTable() {
5408
+ const result = {};
5409
+ this.routingTable.forEach((entry, target) => {
5410
+ result[target] = { ...entry, nextHops: [...entry.nextHops] };
5411
+ });
5412
+ return result;
5413
+ }
5414
+ /**
5415
+ * 获取已知节点列表(兼容旧接口)
5416
+ * @returns 节点 ID 数组
5417
+ */
5418
+ getKnownNodes() {
5419
+ return this.directNodes.map((n) => n.nodeId);
5420
+ }
5421
+ };
5422
+
5423
+ // src/MessageHandler.ts
5424
+ var MessageHandler = class {
5425
+ /** 回调函数集合 */
5426
+ callbacks;
5427
+ /**
5428
+ * 创建消息处理器
5429
+ * @param callbacks 回调函数集合
5430
+ */
5431
+ constructor(callbacks) {
5432
+ this.callbacks = callbacks;
5433
+ }
5434
+ /**
5435
+ * 处理收到的请求
5436
+ * 根据是否有中继消息上下文判断是直连请求还是中继请求
5437
+ * @param from 发送者 Peer ID
5438
+ * @param request 请求数据
5439
+ * @param relayMessage 中继消息上下文(可选,有则为中继请求)
5440
+ * @returns 响应数据
5441
+ */
5442
+ async handleRequest(from, request, relayMessage) {
5443
+ if (relayMessage) {
5444
+ return this.handleRelayRequest(from, request, relayMessage);
5445
+ }
5446
+ return this.handleDirectRequest(from, request);
5447
+ }
5448
+ /**
5449
+ * 处理直连请求
5450
+ * @param from 发送者 Peer ID
5451
+ * @param request 请求数据
5452
+ * @returns 响应数据
5453
+ */
5454
+ async handleDirectRequest(from, request) {
5455
+ const result = await this.processHandler(request.path, from, request.data);
5456
+ if (this.isErrorResponse(result)) {
5457
+ return result;
5458
+ }
5459
+ return { status: 200, data: result };
5460
+ }
5461
+ /**
5462
+ * 处理中继请求
5463
+ * @param from 发送者 Peer ID
5464
+ * @param request 请求数据
5465
+ * @param relayMessage 中继消息上下文
5466
+ * @returns 响应数据
5467
+ */
5468
+ async handleRelayRequest(from, request, relayMessage) {
5469
+ const myPeerId = this.callbacks.getMyPeerId();
5470
+ const { originalTarget, relayPath, forwardPath } = relayMessage;
5471
+ if (myPeerId === originalTarget) {
5472
+ const result = await this.processHandler(request.path, from, request.data);
5473
+ if (this.isErrorResponse(result)) {
5474
+ return result;
5475
+ }
5476
+ return { status: 200, data: result };
5477
+ }
5478
+ const currentTTL = relayMessage.ttl ?? DEFAULT_TTL;
5479
+ if (currentTTL <= 0) {
5480
+ this.callbacks.debugLog("MessageHandler", "ttlExpired", { type: "relay-request", id: relayMessage.id });
5481
+ return {
5482
+ status: 502,
5483
+ data: { error: "TTL expired - message dropped to prevent routing loop" }
5484
+ };
5485
+ }
5486
+ if (forwardPath.length > 0) {
5487
+ const nextHop = forwardPath[0];
5488
+ const remainingPath = forwardPath.slice(1);
5489
+ try {
5490
+ const response = await this.forwardRelay(nextHop, {
5491
+ type: "relay-request",
5492
+ id: relayMessage.id,
5493
+ originalTarget,
5494
+ relayPath: [...relayPath, myPeerId],
5495
+ forwardPath: remainingPath,
5496
+ ttl: currentTTL - 1,
5497
+ request
5498
+ });
5499
+ return response;
5500
+ } catch (err) {
5501
+ return {
5502
+ status: 500,
5503
+ data: { error: `Forward failed: ${err instanceof Error ? err.message : "Unknown error"}` }
5504
+ };
5505
+ }
5506
+ }
5507
+ try {
5508
+ const data = await this.forwardToTarget(originalTarget, request, relayMessage);
5509
+ return { status: 200, data };
5510
+ } catch (err) {
5511
+ return {
5512
+ status: 500,
5513
+ data: { error: `Forward to target failed: ${err instanceof Error ? err.message : "Unknown error"}` }
5514
+ };
5515
+ }
5516
+ }
5517
+ /**
5518
+ * 调用注册的处理器
5519
+ * @param path 请求路径
5520
+ * @param from 发送者 Peer ID
5521
+ * @param data 请求数据
5522
+ * @returns 处理器返回的数据,或 404 错误
5523
+ */
5524
+ async processHandler(path, from, data) {
5525
+ const handlers = this.callbacks.getSimpleHandlers();
5526
+ const simpleHandler = handlers.get(path);
5527
+ if (simpleHandler) {
5528
+ return await simpleHandler(from, data);
5529
+ }
5530
+ return { status: 404, data: { error: `Path not found: ${path}` } };
5531
+ }
5532
+ /**
5533
+ * 判断结果是否为错误响应
5534
+ */
5535
+ isErrorResponse(result) {
5536
+ return typeof result === "object" && result !== null && "status" in result && "data" in result;
5537
+ }
4796
5538
  /**
4797
- * 来电监听器集合
5539
+ * 创建连接并发送中继消息的通用方法
5540
+ * @param targetId 目标节点 ID
5541
+ * @param message 要发送的消息
5542
+ * @param extractResponse 从响应消息中提取数据的函数
5543
+ * @returns Promise<Response>
4798
5544
  */
5545
+ createConnectionAndSend(targetId, message, extractResponse) {
5546
+ return new Promise((resolve, reject) => {
5547
+ this.callbacks.debugLog("MessageHandler", "createConnectionAndSend", { targetId });
5548
+ this.callbacks.waitForReady().then(() => {
5549
+ const peerInstance = this.callbacks.getPeerInstance();
5550
+ if (!peerInstance) {
5551
+ reject(new Error("Peer instance not available"));
5552
+ return;
5553
+ }
5554
+ const conn = peerInstance.connect(targetId, { reliable: true });
5555
+ const timeout = setTimeout(() => {
5556
+ conn.close();
5557
+ reject(new Error(`Connection timeout: ${targetId}`));
5558
+ }, CONNECTION_TIMEOUT_MS);
5559
+ conn.on("open", () => {
5560
+ this.callbacks.debugLog("Conn", "open", targetId);
5561
+ conn.send(message);
5562
+ });
5563
+ conn.on("data", (responseData) => {
5564
+ const response = responseData;
5565
+ const extracted = extractResponse(response);
5566
+ if (extracted) {
5567
+ clearTimeout(timeout);
5568
+ conn.close();
5569
+ resolve(extracted);
5570
+ }
5571
+ });
5572
+ conn.on("error", (err) => {
5573
+ this.callbacks.debugLog("Conn", "error", { peer: targetId, error: err });
5574
+ clearTimeout(timeout);
5575
+ reject(err);
5576
+ });
5577
+ conn.on("close", () => {
5578
+ this.callbacks.debugLog("Conn", "close", targetId);
5579
+ clearTimeout(timeout);
5580
+ reject(new Error("Connection closed"));
5581
+ });
5582
+ }).catch(reject);
5583
+ });
5584
+ }
5585
+ /**
5586
+ * 转发中继请求到下一个节点
5587
+ * @param nextHop 下一跳节点 ID
5588
+ * @param message 要转发的消息
5589
+ * @returns 响应数据
5590
+ */
5591
+ async forwardRelay(nextHop, message) {
5592
+ return this.createConnectionAndSend(
5593
+ nextHop,
5594
+ message,
5595
+ (response) => {
5596
+ if (response.type === "relay-response" && response.response) {
5597
+ return response.response;
5598
+ }
5599
+ return null;
5600
+ }
5601
+ );
5602
+ }
5603
+ /**
5604
+ * 转发到最终目标节点(当没有更多中继节点时使用)
5605
+ * @param targetId 目标节点 ID
5606
+ * @param request 请求数据
5607
+ * @param originalMessage 原始中继消息
5608
+ * @returns 响应数据
5609
+ */
5610
+ async forwardToTarget(targetId, request, originalMessage) {
5611
+ const myPeerId = this.callbacks.getMyPeerId();
5612
+ const currentTTL = originalMessage.ttl ?? DEFAULT_TTL;
5613
+ const message = {
5614
+ type: "relay-request",
5615
+ id: originalMessage.id,
5616
+ originalTarget: originalMessage.originalTarget,
5617
+ relayPath: [...originalMessage.relayPath, myPeerId],
5618
+ forwardPath: [],
5619
+ ttl: currentTTL - 1,
5620
+ request
5621
+ };
5622
+ const response = await this.createConnectionAndSend(
5623
+ targetId,
5624
+ message,
5625
+ (resp) => {
5626
+ if (resp.type === "relay-response" && resp.response) {
5627
+ return resp.response;
5628
+ }
5629
+ return null;
5630
+ }
5631
+ );
5632
+ return response.data;
5633
+ }
5634
+ };
5635
+
5636
+ // src/PeerJsWrapper.ts
5637
+ var VERSION = "1.2.0";
5638
+ console.log(`[sx-peerjs-http-util] v${VERSION}`);
5639
+ function generateUUID() {
5640
+ return crypto.randomUUID();
5641
+ }
5642
+ var PeerJsWrapper = class _PeerJsWrapper {
5643
+ /** 本地 Peer ID */
5644
+ myPeerId;
5645
+ /** PeerJS 实例 */
5646
+ peerInstance = null;
5647
+ /** 当前活跃的传入连接集合 */
5648
+ connections = /* @__PURE__ */ new Set();
5649
+ /** 待处理的请求映射表(用于请求-响应匹配) */
5650
+ pendingRequests = /* @__PURE__ */ new Map();
5651
+ /** 路径处理器映射表 */
5652
+ simpleHandlers = /* @__PURE__ */ new Map();
5653
+ /** 重连定时器 */
5654
+ reconnectTimer = null;
5655
+ /** 是否已销毁 */
5656
+ isDestroyed = false;
5657
+ /** 是否开启调试模式 */
5658
+ isDebug;
5659
+ /** 服务器配置 */
5660
+ serverConfig;
5661
+ /** 当前活跃的通话 */
5662
+ activeCall = null;
5663
+ /** 来电监听器集合 */
4799
5664
  incomingCallListeners = /* @__PURE__ */ new Set();
5665
+ /** 路由管理器 */
5666
+ router;
5667
+ /** 消息处理器 */
5668
+ messageHandler;
4800
5669
  /**
4801
5670
  * 创建 PeerJsWrapper 实例
4802
5671
  * @param peerId 可选的 Peer ID,如果不提供则自动生成 UUID
4803
5672
  * @param isDebug 是否开启调试模式,开启后会打印事件日志
4804
5673
  * @param server 可选的信令服务器配置,不提供则使用 PeerJS 公共服务器
5674
+ * @param relayConfig 可选的中继配置
4805
5675
  */
4806
- constructor(peerId, isDebug, server) {
5676
+ constructor(peerId, isDebug, server, relayConfig) {
4807
5677
  this.myPeerId = peerId || generateUUID();
4808
5678
  this.isDebug = isDebug ?? false;
4809
5679
  this.serverConfig = server;
5680
+ const callbacks = {
5681
+ getMyPeerId: () => this.myPeerId,
5682
+ getPeerInstance: () => this.peerInstance,
5683
+ debugLog: this.debugLog.bind(this),
5684
+ sendRelayMessage: (targetId, message) => this.sendRelayMessage(targetId, message)
5685
+ };
5686
+ this.router = new Router(callbacks, relayConfig);
5687
+ this.router.init();
5688
+ this.messageHandler = new MessageHandler({
5689
+ ...callbacks,
5690
+ waitForReady: () => this.waitForReady(),
5691
+ getSimpleHandlers: () => this.simpleHandlers,
5692
+ onRouteUpdate: (fromPeerId, message) => this.router.handleRouteUpdate(fromPeerId, message)
5693
+ });
4810
5694
  this.connect();
4811
5695
  }
4812
5696
  /**
4813
- * 调试日志输出
4814
- * @param obj 对象名
4815
- * @param event 事件名
4816
- * @param data 事件数据
5697
+ * 创建实例并等待就绪(语法糖)
5698
+ * @param peerId 可选的 Peer ID
5699
+ * @param isDebug 是否开启调试模式
5700
+ * @param server 可选的信令服务器配置
5701
+ * @param relayConfig 可选的中继配置
5702
+ * @returns Promise<PeerJsWrapper>
4817
5703
  */
5704
+ static async create(peerId, isDebug, server, relayConfig) {
5705
+ const wrapper = new _PeerJsWrapper(peerId, isDebug, server, relayConfig);
5706
+ await wrapper.whenReady();
5707
+ return wrapper;
5708
+ }
4818
5709
  debugLog(obj, event, data) {
4819
5710
  if (this.isDebug) {
4820
- const dataStr = data !== void 0 ? typeof data === "object" ? JSON.stringify(data) : String(data) : "";
4821
- console.log(`${obj} ${event} ${dataStr}`);
5711
+ console.log(obj, event, data);
4822
5712
  }
4823
5713
  }
4824
- /**
4825
- * 连接到 PeerJS 服务器
4826
- */
4827
5714
  connect() {
4828
5715
  if (this.isDestroyed) return;
4829
5716
  this.peerInstance = this.serverConfig ? new $416260bce337df90$export$ecd1fc136c422448(this.myPeerId, { ...this.serverConfig }) : new $416260bce337df90$export$ecd1fc136c422448(this.myPeerId);
4830
5717
  this.setupPeerEventHandlers();
4831
5718
  }
4832
- /**
4833
- * 设置 Peer 实例的事件处理器
4834
- */
4835
5719
  setupPeerEventHandlers() {
4836
5720
  if (!this.peerInstance) return;
4837
5721
  this.peerInstance.on("open", (id) => {
@@ -4859,20 +5743,14 @@ var PeerJsHttpUtil = (() => {
4859
5743
  });
4860
5744
  this.setupIncomingConnectionHandler();
4861
5745
  }
4862
- /**
4863
- * 安排重连
4864
- */
4865
5746
  scheduleReconnect() {
4866
5747
  if (this.isDestroyed) return;
4867
5748
  if (this.reconnectTimer) return;
4868
5749
  this.reconnectTimer = setTimeout(() => {
4869
5750
  this.reconnectTimer = null;
4870
5751
  this.reconnect();
4871
- }, 1e3);
5752
+ }, RECONNECT_DELAY_MS);
4872
5753
  }
4873
- /**
4874
- * 执行重连
4875
- */
4876
5754
  reconnect() {
4877
5755
  if (this.isDestroyed) return;
4878
5756
  this.debugLog("PeerJsWrapper", "reconnect");
@@ -4885,23 +5763,12 @@ var PeerJsHttpUtil = (() => {
4885
5763
  }
4886
5764
  this.connect();
4887
5765
  }
4888
- /**
4889
- * 获取当前 Peer ID
4890
- * @returns string 当前 Peer ID
4891
- */
4892
5766
  getPeerId() {
4893
5767
  return this.myPeerId;
4894
5768
  }
4895
- /**
4896
- * 等待 Peer 连接就绪(连接到信令服务器)
4897
- * @returns Promise<void> 当连接成功时 resolve
4898
- */
4899
5769
  whenReady() {
4900
5770
  return this.waitForReady();
4901
5771
  }
4902
- /**
4903
- * 等待 Peer 连接就绪
4904
- */
4905
5772
  waitForReady() {
4906
5773
  return new Promise((resolve, reject) => {
4907
5774
  if (!this.peerInstance) {
@@ -4926,87 +5793,363 @@ var PeerJsHttpUtil = (() => {
4926
5793
  this.peerInstance.on("error", onError);
4927
5794
  });
4928
5795
  }
5796
+ getRoutingTable() {
5797
+ return this.router.getRoutingTable();
5798
+ }
5799
+ getKnownNodes() {
5800
+ return this.router.getKnownNodes();
5801
+ }
5802
+ /**
5803
+ * 发送中继消息的辅助方法
5804
+ * @param targetId 目标节点 ID
5805
+ * @param message 中继消息
5806
+ */
5807
+ sendRelayMessage(targetId, message) {
5808
+ return new Promise((resolve, reject) => {
5809
+ if (!this.peerInstance) {
5810
+ reject(new Error("Peer instance not available"));
5811
+ return;
5812
+ }
5813
+ const conn = this.peerInstance.connect(targetId, { reliable: true });
5814
+ const timeout = setTimeout(() => {
5815
+ conn.close();
5816
+ reject(new Error(`Send to ${targetId} timeout`));
5817
+ }, SEND_TIMEOUT_MS);
5818
+ conn.on("open", () => {
5819
+ conn.send(message);
5820
+ clearTimeout(timeout);
5821
+ conn.close();
5822
+ resolve();
5823
+ });
5824
+ conn.on("error", () => {
5825
+ clearTimeout(timeout);
5826
+ reject(new Error(`Send to ${targetId} failed`));
5827
+ });
5828
+ conn.on("close", () => {
5829
+ clearTimeout(timeout);
5830
+ resolve();
5831
+ });
5832
+ });
5833
+ }
4929
5834
  /**
4930
- * 发送请求到指定 Peer
4931
- * @param peerId 对端设备 ID
5835
+ * 尝试直连目标节点
5836
+ * @param targetId 目标节点 ID
4932
5837
  * @param path 请求路径
4933
5838
  * @param data 请求数据
4934
- * @returns Promise<unknown> 返回响应数据(自动拆箱,只返回 data 部分)
5839
+ * @param requestId 请求 ID
5840
+ * @returns Promise<unknown> - 响应数据
4935
5841
  */
4936
- send(peerId, path, data) {
5842
+ tryDirectConnect(targetId, path, data, requestId) {
4937
5843
  return new Promise((resolve, reject) => {
4938
- this.debugLog("PeerJsWrapper", "send", { peerId, path, data });
5844
+ if (!this.peerInstance) {
5845
+ reject(new Error("Peer instance not available"));
5846
+ return;
5847
+ }
5848
+ const startTime = Date.now();
5849
+ const conn = this.peerInstance.connect(targetId, { reliable: true });
5850
+ let resolved = false;
5851
+ const timeout = setTimeout(() => {
5852
+ if (!resolved) {
5853
+ resolved = true;
5854
+ conn.close();
5855
+ reject(new Error(`Request timeout: ${targetId}${path}`));
5856
+ }
5857
+ }, CONNECTION_TIMEOUT_MS);
5858
+ conn.on("open", () => {
5859
+ const latency = Date.now() - startTime;
5860
+ this.debugLog("Conn", "open", { peer: targetId, latency });
5861
+ const request = { path, data };
5862
+ const message = {
5863
+ type: "request",
5864
+ id: requestId,
5865
+ request
5866
+ };
5867
+ conn.send(message);
5868
+ });
5869
+ conn.on("data", (responseData) => {
5870
+ this.debugLog("Conn", "data", { peer: targetId, data: responseData });
5871
+ const message = responseData;
5872
+ if (message.type === "response" && message.id === requestId) {
5873
+ if (!resolved) {
5874
+ resolved = true;
5875
+ clearTimeout(timeout);
5876
+ const response = message.response;
5877
+ if (response.status < 200 || response.status >= 300) {
5878
+ conn.close();
5879
+ reject(new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`));
5880
+ } else {
5881
+ const latency = Date.now() - startTime;
5882
+ this.router.recordDirectNode(targetId, latency);
5883
+ this.router.broadcastRouteUpdate();
5884
+ resolve(response.data);
5885
+ }
5886
+ }
5887
+ }
5888
+ });
5889
+ conn.on("error", (err) => {
5890
+ if (!resolved) {
5891
+ resolved = true;
5892
+ clearTimeout(timeout);
5893
+ this.debugLog("Conn", "error", { peer: targetId, error: err });
5894
+ reject(err);
5895
+ }
5896
+ });
5897
+ conn.on("close", () => {
5898
+ if (!resolved) {
5899
+ resolved = true;
5900
+ clearTimeout(timeout);
5901
+ this.debugLog("Conn", "close", targetId);
5902
+ reject(new Error("Connection closed"));
5903
+ }
5904
+ });
5905
+ });
5906
+ }
5907
+ /**
5908
+ * 通过中继节点转发请求
5909
+ * @param nextHopId 下一跳节点 ID
5910
+ * @param targetId 原始目标节点 ID
5911
+ * @param path 请求路径
5912
+ * @param data 请求数据
5913
+ * @returns Promise<unknown> - 响应数据
5914
+ */
5915
+ relayVia(nextHopId, targetId, path, data) {
5916
+ return new Promise((resolve, reject) => {
5917
+ this.debugLog("PeerJsWrapper", "relayVia", { targetId, nextHop: nextHopId });
4939
5918
  this.waitForReady().then(() => {
4940
5919
  if (!this.peerInstance) {
4941
5920
  reject(new Error("Peer instance not available"));
4942
5921
  return;
4943
5922
  }
4944
- const conn = this.peerInstance.connect(peerId, {
4945
- reliable: true
4946
- });
5923
+ const conn = this.peerInstance.connect(nextHopId, { reliable: true });
5924
+ const startTime = Date.now();
4947
5925
  const timeout = setTimeout(() => {
4948
5926
  conn.close();
4949
- this.pendingRequests.delete(requestId);
4950
- reject(new Error(`Request timeout: ${peerId}${path}`));
4951
- }, 3e4);
4952
- const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;
4953
- this.pendingRequests.set(requestId, { resolve, reject, timeout });
5927
+ reject(new Error(`Relay timeout: ${nextHopId}${path}`));
5928
+ }, CONNECTION_TIMEOUT_MS);
4954
5929
  conn.on("open", () => {
4955
- this.debugLog("Conn", "open", peerId);
5930
+ this.debugLog("Conn", "open", nextHopId);
4956
5931
  const request = { path, data };
4957
5932
  const message = {
4958
- type: "request",
4959
- id: requestId,
5933
+ type: "relay-request",
5934
+ id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,
5935
+ originalTarget: targetId,
5936
+ relayPath: [this.myPeerId],
5937
+ forwardPath: [],
5938
+ ttl: DEFAULT_TTL,
4960
5939
  request
4961
5940
  };
4962
5941
  conn.send(message);
4963
5942
  });
4964
5943
  conn.on("data", (responseData) => {
4965
- this.debugLog("Conn", "data", { peer: peerId, data: responseData });
4966
5944
  const message = responseData;
4967
- if (message.type === "response" && message.id === requestId) {
4968
- const pending = this.pendingRequests.get(requestId);
4969
- if (pending) {
4970
- clearTimeout(pending.timeout);
4971
- this.pendingRequests.delete(requestId);
4972
- const response = message.response;
5945
+ if (message.type === "relay-response") {
5946
+ clearTimeout(timeout);
5947
+ conn.close();
5948
+ const response = message.response;
5949
+ if (response) {
4973
5950
  if (response.status < 200 || response.status >= 300) {
4974
- pending.reject(
4975
- new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`)
4976
- );
5951
+ reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));
4977
5952
  } else {
4978
- pending.resolve(response.data);
5953
+ const latency = Date.now() - startTime;
5954
+ this.router.recordDirectNode(nextHopId, latency);
5955
+ this.router.broadcastRouteUpdate();
5956
+ resolve(response.data);
4979
5957
  }
4980
5958
  }
4981
- conn.close();
5959
+ } else if (message.type === "route-update") {
5960
+ this.router.handleRouteUpdate(nextHopId, message);
5961
+ } else if (message.type === "route-query") {
5962
+ this.router.handleRouteQuery(nextHopId, message);
5963
+ } else if (message.type === "route-response") {
5964
+ this.router.handleRouteResponse(nextHopId, message);
4982
5965
  }
4983
5966
  });
4984
5967
  conn.on("error", (err) => {
4985
- this.debugLog("Conn", "error", { peer: peerId, error: err });
4986
- const pending = this.pendingRequests.get(requestId);
4987
- if (pending) {
4988
- clearTimeout(pending.timeout);
4989
- this.pendingRequests.delete(requestId);
4990
- pending.reject(err);
4991
- }
5968
+ this.debugLog("Conn", "error", { peer: nextHopId, error: err });
5969
+ clearTimeout(timeout);
5970
+ reject(err);
4992
5971
  });
4993
5972
  conn.on("close", () => {
4994
- this.debugLog("Conn", "close", peerId);
4995
- const pending = this.pendingRequests.get(requestId);
4996
- if (pending) {
4997
- clearTimeout(pending.timeout);
4998
- this.pendingRequests.delete(requestId);
4999
- pending.reject(new Error("Connection closed"));
5000
- }
5973
+ this.debugLog("Conn", "close", nextHopId);
5974
+ clearTimeout(timeout);
5975
+ reject(new Error("Relay connection closed"));
5001
5976
  });
5002
- }).catch((err) => {
5003
- reject(err);
5004
- });
5977
+ }).catch(reject);
5978
+ });
5979
+ }
5980
+ /**
5981
+ * 自动路由发送
5982
+ *
5983
+ * 1. 查路由表 → 有路由 → 尝试中继 → 全部失败 → 降级直连 → 失败 → 结束
5984
+ * 2. 路由表无目标 → 直连 → 失败 → 结束
5985
+ * @param peerId 目标节点 ID
5986
+ * @param path 请求路径
5987
+ * @param data 请求数据
5988
+ * @returns Promise<unknown> - 响应数据
5989
+ */
5990
+ send(peerId, path, data) {
5991
+ const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;
5992
+ return new Promise((resolve, reject) => {
5993
+ this.debugLog("PeerJsWrapper", "send", { peerId, path, data });
5994
+ const nextHops = this.router.getNextHopsToTarget(peerId);
5995
+ if (nextHops.length > 0) {
5996
+ this.tryRelayChain(peerId, path, data, nextHops, 0).then(resolve).catch((relayErr) => {
5997
+ this.debugLog("PeerJsWrapper", "relayFailed", { peerId, error: relayErr.message });
5998
+ this.fallbackToDirect(peerId, path, data, requestId).then(resolve).catch(reject);
5999
+ });
6000
+ } else {
6001
+ this.tryDirectConnect(peerId, path, data, requestId).then(resolve).catch((directErr) => {
6002
+ this.debugLog("PeerJsWrapper", "directFailed", { peerId, error: directErr.message });
6003
+ this.handleSendError(peerId, directErr).then(resolve).catch(reject);
6004
+ });
6005
+ }
6006
+ });
6007
+ }
6008
+ /**
6009
+ * 处理发送错误
6010
+ * @param peerId 目标节点 ID
6011
+ * @param error 错误对象
6012
+ * @returns Promise
6013
+ */
6014
+ async handleSendError(peerId, error) {
6015
+ const errorMsg = error.message;
6016
+ const isHttpError = errorMsg.includes("Request failed:") || errorMsg.includes("404") || errorMsg.includes("500");
6017
+ const isConnectionError = errorMsg.includes("timeout") || errorMsg.includes("Connection closed") || errorMsg.includes("Peer instance not available");
6018
+ if (isHttpError) {
6019
+ throw error;
6020
+ }
6021
+ if (!isConnectionError) {
6022
+ throw error;
6023
+ }
6024
+ this.router.removeRoute(peerId);
6025
+ this.debugLog("PeerJsWrapper", "routeRemoved", peerId);
6026
+ if (!this.router.isRoutingTableEmpty()) {
6027
+ return this.performRouteDiscovery(peerId, "", void 0, "");
6028
+ }
6029
+ throw new Error(`Cannot reach ${peerId}: no route found and routing table is empty`);
6030
+ }
6031
+ /**
6032
+ * 降级到直连尝试
6033
+ * @param peerId 目标节点 ID
6034
+ * @param path 请求路径
6035
+ * @param data 请求数据
6036
+ * @param requestId 请求 ID
6037
+ * @returns Promise
6038
+ */
6039
+ async fallbackToDirect(peerId, path, data, requestId) {
6040
+ this.debugLog("PeerJsWrapper", "fallbackToDirect", peerId);
6041
+ return this.tryDirectConnect(peerId, path, data, requestId).catch((directErr) => {
6042
+ this.debugLog("PeerJsWrapper", "directFailed", { peerId, error: directErr.message });
6043
+ this.handleSendError(peerId, directErr);
5005
6044
  });
5006
6045
  }
5007
6046
  /**
5008
- * 设置传入连接处理器
6047
+ * 尝试通过中继链转发
6048
+ * @param targetId 目标节点 ID
6049
+ * @param path 请求路径
6050
+ * @param data 请求数据
6051
+ * @param nextHops 下一跳列表
6052
+ * @param index 当前尝试的下一跳索引
6053
+ * @returns Promise<unknown>
6054
+ */
6055
+ tryRelayChain(targetId, path, data, nextHops, index) {
6056
+ if (index >= nextHops.length) {
6057
+ return Promise.reject(new Error("All relay nodes failed"));
6058
+ }
6059
+ const nextHop = nextHops[index];
6060
+ this.debugLog("PeerJsWrapper", "tryRelay", { targetId, nextHop: nextHop.nodeId, latency: nextHop.latency });
6061
+ return this.relayVia(nextHop.nodeId, targetId, path, data).catch(() => {
6062
+ return this.tryRelayChain(targetId, path, data, nextHops, index + 1);
6063
+ });
6064
+ }
6065
+ /**
6066
+ * 执行路由发现
6067
+ * @param targetId 目标节点 ID
6068
+ * @param path 请求路径
6069
+ * @param data 请求数据
6070
+ * @param requestId 请求 ID
6071
+ * @returns Promise<unknown>
6072
+ */
6073
+ async performRouteDiscovery(targetId, path, data, requestId) {
6074
+ this.debugLog("PeerJsWrapper", "routeDiscovery", { targetId });
6075
+ const routeEntry = await this.router.discoverRoute(targetId);
6076
+ if (!routeEntry || routeEntry.nextHops.length === 0) {
6077
+ throw new Error(`Cannot reach ${targetId}: no route found`);
6078
+ }
6079
+ this.debugLog("PeerJsWrapper", "routeFound", { targetId, nextHops: routeEntry.nextHops });
6080
+ return this.tryRelayChain(targetId, path, data, routeEntry.nextHops, 0);
6081
+ }
6082
+ /**
6083
+ * 中继发送(内部方法,不对外暴露)
6084
+ * @param targetId 目标节点 ID
6085
+ * @param path 请求路径
6086
+ * @param data 请求数据
6087
+ * @param relayNodes 手动指定的中继节点(可选,不指定则自动路由)
6088
+ * @returns Promise<unknown>
5009
6089
  */
6090
+ relaySend(targetId, path, data, relayNodes) {
6091
+ if (!relayNodes || relayNodes.length === 0) {
6092
+ return this.send(targetId, path, data);
6093
+ }
6094
+ const [firstRelay, ...remainingRelays] = relayNodes;
6095
+ return new Promise((resolve, reject) => {
6096
+ this.debugLog("PeerJsWrapper", "relaySend", { targetId, firstRelay, remainingRelays });
6097
+ this.waitForReady().then(() => {
6098
+ if (!this.peerInstance) {
6099
+ reject(new Error("Peer instance not available"));
6100
+ return;
6101
+ }
6102
+ const conn = this.peerInstance.connect(firstRelay, { reliable: true });
6103
+ const timeout = setTimeout(() => {
6104
+ conn.close();
6105
+ reject(new Error(`Relay timeout: ${firstRelay}${path}`));
6106
+ }, CONNECTION_TIMEOUT_MS);
6107
+ conn.on("open", () => {
6108
+ this.debugLog("Conn", "open", firstRelay);
6109
+ const request = { path, data };
6110
+ const message = {
6111
+ type: "relay-request",
6112
+ id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,
6113
+ originalTarget: targetId,
6114
+ relayPath: [],
6115
+ forwardPath: remainingRelays,
6116
+ ttl: DEFAULT_TTL,
6117
+ request
6118
+ };
6119
+ conn.send(message);
6120
+ });
6121
+ conn.on("data", (responseData) => {
6122
+ const message = responseData;
6123
+ if (message.type === "relay-response") {
6124
+ clearTimeout(timeout);
6125
+ conn.close();
6126
+ const response = message.response;
6127
+ if (response) {
6128
+ if (response.status < 200 || response.status >= 300) {
6129
+ reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));
6130
+ } else {
6131
+ this.router.recordSuccessfulNode(firstRelay);
6132
+ this.router.broadcastRouteUpdate();
6133
+ resolve(response.data);
6134
+ }
6135
+ }
6136
+ } else if (message.type === "route-update") {
6137
+ this.router.handleRouteUpdate(firstRelay, message);
6138
+ }
6139
+ });
6140
+ conn.on("error", (err) => {
6141
+ this.debugLog("Conn", "error", { peer: firstRelay, error: err });
6142
+ clearTimeout(timeout);
6143
+ reject(err);
6144
+ });
6145
+ conn.on("close", () => {
6146
+ this.debugLog("Conn", "close", firstRelay);
6147
+ clearTimeout(timeout);
6148
+ reject(new Error("Relay connection closed"));
6149
+ });
6150
+ }).catch(reject);
6151
+ });
6152
+ }
5010
6153
  setupIncomingConnectionHandler() {
5011
6154
  if (!this.peerInstance) return;
5012
6155
  this.peerInstance.on("connection", (conn) => {
@@ -5018,26 +6161,60 @@ var PeerJsHttpUtil = (() => {
5018
6161
  conn.on("data", async (data) => {
5019
6162
  this.debugLog("Conn", "data", { peer: conn.peer, data });
5020
6163
  const message = data;
5021
- if (message.type === "request" && message.request) {
5022
- try {
5023
- const response = await this.handleRequest(conn.peer, message.request);
5024
- const responseMessage = {
5025
- type: "response",
5026
- id: message.id,
5027
- response
5028
- };
5029
- conn.send(responseMessage);
5030
- } catch (error) {
5031
- const errorResponse = {
5032
- type: "response",
5033
- id: message.id,
5034
- response: {
5035
- status: 500,
5036
- data: { error: error instanceof Error ? error.message : "Unknown error" }
5037
- }
5038
- };
5039
- conn.send(errorResponse);
6164
+ if (message.type === "request") {
6165
+ const internalMsg = message;
6166
+ if (internalMsg.request) {
6167
+ try {
6168
+ const response = await this.messageHandler.handleRequest(conn.peer, internalMsg.request);
6169
+ const responseMessage = {
6170
+ type: "response",
6171
+ id: internalMsg.id,
6172
+ response
6173
+ };
6174
+ conn.send(responseMessage);
6175
+ } catch (error) {
6176
+ const errorResponse = {
6177
+ type: "response",
6178
+ id: internalMsg.id,
6179
+ response: {
6180
+ status: 500,
6181
+ data: { error: error instanceof Error ? error.message : "Unknown error" }
6182
+ }
6183
+ };
6184
+ conn.send(errorResponse);
6185
+ }
5040
6186
  }
6187
+ } else if (message.type === "relay-request") {
6188
+ const relayMsg = message;
6189
+ if (relayMsg.request) {
6190
+ try {
6191
+ const response = await this.messageHandler.handleRequest(conn.peer, relayMsg.request, relayMsg);
6192
+ const responseMessage = {
6193
+ type: "relay-response",
6194
+ id: relayMsg.id,
6195
+ originalTarget: relayMsg.originalTarget,
6196
+ relayPath: relayMsg.relayPath,
6197
+ forwardPath: [],
6198
+ response
6199
+ };
6200
+ conn.send(responseMessage);
6201
+ } catch (error) {
6202
+ const errorResponse = {
6203
+ type: "relay-response",
6204
+ id: relayMsg.id,
6205
+ originalTarget: relayMsg.originalTarget,
6206
+ relayPath: relayMsg.relayPath,
6207
+ forwardPath: [],
6208
+ response: {
6209
+ status: 500,
6210
+ data: { error: error instanceof Error ? error.message : "Unknown error" }
6211
+ }
6212
+ };
6213
+ conn.send(errorResponse);
6214
+ }
6215
+ }
6216
+ } else if (message.type === "route-update") {
6217
+ this.router.handleRouteUpdate(conn.peer, message);
5041
6218
  }
5042
6219
  });
5043
6220
  conn.on("close", () => {
@@ -5050,13 +6227,6 @@ var PeerJsHttpUtil = (() => {
5050
6227
  });
5051
6228
  });
5052
6229
  }
5053
- // ============== 语音/视频通话相关方法 ==============
5054
- /**
5055
- * 发起语音/视频通话
5056
- * @param peerId 对端设备 ID
5057
- * @param options 通话选项
5058
- * @returns Promise<CallSession> 通话会话对象
5059
- */
5060
6230
  call(peerId, options) {
5061
6231
  return new Promise((resolve, reject) => {
5062
6232
  this.debugLog("PeerJsWrapper", "call", { peerId, options });
@@ -5121,30 +6291,15 @@ var PeerJsHttpUtil = (() => {
5121
6291
  }).catch(reject);
5122
6292
  });
5123
6293
  }
5124
- /**
5125
- * 注册来电监听器
5126
- * @param listener 来电回调函数
5127
- */
5128
6294
  onIncomingCall(listener) {
5129
6295
  this.incomingCallListeners.add(listener);
5130
6296
  }
5131
- /**
5132
- * 移除来电监听器
5133
- * @param listener 来电回调函数
5134
- */
5135
6297
  offIncomingCall(listener) {
5136
6298
  this.incomingCallListeners.delete(listener);
5137
6299
  }
5138
- /**
5139
- * 获取当前活跃的通话
5140
- * @returns CallSession | null 当前通话会话,无通话时返回 null
5141
- */
5142
6300
  getActiveCall() {
5143
6301
  return this.activeCall;
5144
6302
  }
5145
- /**
5146
- * 设置 MediaConnection 事件处理器
5147
- */
5148
6303
  setupMediaConnectionHandlers(session, mediaConnection) {
5149
6304
  mediaConnection.on("stream", (remoteStream) => {
5150
6305
  this.debugLog("MediaConnection", "stream", { peer: mediaConnection.peer });
@@ -5162,17 +6317,11 @@ var PeerJsHttpUtil = (() => {
5162
6317
  session.setState("ended", err.message || "Media error");
5163
6318
  });
5164
6319
  }
5165
- /**
5166
- * 清理通话资源
5167
- */
5168
6320
  cleanupCall(session) {
5169
6321
  if (this.activeCall === session) {
5170
6322
  this.activeCall = null;
5171
6323
  }
5172
6324
  }
5173
- /**
5174
- * 处理来电
5175
- */
5176
6325
  handleIncomingCall(mediaConnection) {
5177
6326
  this.debugLog("Peer", "call", { from: mediaConnection.peer, metadata: mediaConnection.metadata });
5178
6327
  const metadata = mediaConnection.metadata;
@@ -5222,40 +6371,12 @@ var PeerJsHttpUtil = (() => {
5222
6371
  }
5223
6372
  });
5224
6373
  }
5225
- /**
5226
- * 注册简化处理器(直接返回数据,自动装箱)
5227
- * @param path 请求路径
5228
- * @param handler 处理器函数,接收请求数据,直接返回响应数据
5229
- */
5230
6374
  registerHandler(path, handler) {
5231
6375
  this.simpleHandlers.set(path, handler);
5232
6376
  }
5233
- /**
5234
- * 注销简化处理器
5235
- * @param path 请求路径
5236
- */
5237
6377
  unregisterHandler(path) {
5238
6378
  this.simpleHandlers.delete(path);
5239
6379
  }
5240
- /**
5241
- * 内部请求处理方法
5242
- * @param from 发送者的 Peer ID
5243
- * @param request 请求数据
5244
- */
5245
- async handleRequest(from, request) {
5246
- const simpleHandler = this.simpleHandlers.get(request.path);
5247
- if (simpleHandler) {
5248
- const data = await simpleHandler(from, request.data);
5249
- return { status: 200, data };
5250
- }
5251
- return {
5252
- status: 404,
5253
- data: { error: `Path not found: ${request.path}` }
5254
- };
5255
- }
5256
- /**
5257
- * 关闭所有连接并销毁 Peer 实例
5258
- */
5259
6380
  destroy() {
5260
6381
  this.isDestroyed = true;
5261
6382
  if (this.reconnectTimer) {
@@ -5281,6 +6402,10 @@ var PeerJsHttpUtil = (() => {
5281
6402
  this.peerInstance.destroy();
5282
6403
  this.peerInstance = null;
5283
6404
  }
6405
+ if (this.router) {
6406
+ this.router.persist();
6407
+ this.router.destroy();
6408
+ }
5284
6409
  }
5285
6410
  };
5286
6411
  return __toCommonJS(index_exports);