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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/PeerJsWrapper.ts", "../src/CallSession.ts", "../src/Routing.ts", "../src/MessageHandler.ts"],
4
- "sourcesContent": ["/**\n * PeerJsWrapper - \u5C01\u88C5 PeerJS \u4E3A\u7C7B\u4F3C HTTP \u7684 API\n * \n * \u6838\u5FC3\u529F\u80FD\uFF1A\n * 1. \u7C7B\u4F3C HTTP \u7684\u8BF7\u6C42/\u54CD\u5E94\u673A\u5236\uFF08send/relaySend\uFF09\n * 2. \u8BED\u97F3/\u89C6\u9891\u901A\u8BDD\uFF08call/onIncomingCall\uFF09\n * 3. \u4E2D\u7EE7\u8DEF\u7531\uFF08relaySend/getRoutingTable/getKnownNodes\uFF09\n * \n * @example\n * // \u57FA\u672C\u8BF7\u6C42\n * const wrapper = new PeerJsWrapper();\n * const data = await wrapper.send(peerId, '/api/hello', { name: 'world' });\n * \n * // \u4E2D\u7EE7\u8BF7\u6C42\n * await wrapper.relaySend(targetId, '/api/data', 'test', ['relayNode1', 'relayNode2']);\n * \n * // \u8BED\u97F3\u901A\u8BDD\n * const call = await wrapper.call(peerId, { video: true });\n */\n\nimport { Peer, DataConnection, MediaConnection } from 'peerjs';\nimport { CallSessionImpl } from './CallSession';\nimport { RoutingManager } from './Routing';\nimport { MessageHandler } from './MessageHandler';\nimport type {\n Request,\n Response,\n SimpleHandler,\n CallOptions,\n CallSession,\n CallState,\n CallStateListener,\n IncomingCallEvent,\n IncomingCallListener,\n RouteEntry,\n RelayConfig,\n RelayMessage,\n ServerConfig\n} from './types';\n\n/** \u7248\u672C\u53F7\uFF08\u6784\u5EFA\u65F6\u6CE8\u5165\uFF09 */\ndeclare const __VERSION__: string;\nexport const VERSION = __VERSION__;\nconsole.log(`[sx-peerjs-http-util] v${VERSION}`);\n\n/**\n * \u751F\u6210 UUID v4\n */\nfunction generateUUID(): string {\n return crypto.randomUUID();\n}\n\n/**\n * \u5F85\u5904\u7406\u7684\u8BF7\u6C42\n */\ninterface PendingRequest {\n /** \u6210\u529F\u56DE\u8C03 */\n resolve: (data: unknown) => void;\n /** \u5931\u8D25\u56DE\u8C03 */\n reject: (error: Error) => void;\n /** \u8D85\u65F6\u5B9A\u65F6\u5668 */\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * \u5185\u90E8\u6D88\u606F\u683C\u5F0F\uFF08\u7528\u4E8E\u76F4\u8FDE\u8BF7\u6C42\uFF09\n */\ninterface InternalMessage {\n /** \u6D88\u606F\u7C7B\u578B */\n type: 'request' | 'response';\n /** \u6D88\u606F ID\uFF0C\u7528\u4E8E\u5339\u914D\u8BF7\u6C42\u548C\u54CD\u5E94 */\n id: string;\n /** \u8BF7\u6C42\u6570\u636E */\n request?: Request;\n /** \u54CD\u5E94\u6570\u636E */\n response?: Response;\n}\n\n/**\n * PeerJsWrapper \u4E3B\u7C7B\n * \u5C01\u88C5 PeerJS\uFF0C\u63D0\u4F9B\u7C7B\u4F3C HTTP \u7684 API\n */\nexport class PeerJsWrapper {\n /** \u672C\u5730 Peer ID */\n private myPeerId: string;\n /** PeerJS \u5B9E\u4F8B */\n private peerInstance: Peer | null = null;\n /** \u5F53\u524D\u6D3B\u8DC3\u7684\u4F20\u5165\u8FDE\u63A5\u96C6\u5408 */\n private connections = new Set<DataConnection>();\n /** \u5F85\u5904\u7406\u7684\u8BF7\u6C42\u6620\u5C04\u8868\uFF08\u7528\u4E8E\u8BF7\u6C42-\u54CD\u5E94\u5339\u914D\uFF09 */\n private pendingRequests = new Map<string, PendingRequest>();\n /** \u8DEF\u5F84\u5904\u7406\u5668\u6620\u5C04\u8868 */\n private simpleHandlers = new Map<string, SimpleHandler>();\n /** \u91CD\u8FDE\u5B9A\u65F6\u5668 */\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n /** \u662F\u5426\u5DF2\u9500\u6BC1 */\n private isDestroyed = false;\n /** \u662F\u5426\u5F00\u542F\u8C03\u8BD5\u6A21\u5F0F */\n private isDebug: boolean;\n /** \u670D\u52A1\u5668\u914D\u7F6E */\n private serverConfig?: ServerConfig;\n /** \u5F53\u524D\u6D3B\u8DC3\u7684\u901A\u8BDD */\n private activeCall: CallSessionImpl | null = null;\n /** \u6765\u7535\u76D1\u542C\u5668\u96C6\u5408 */\n private incomingCallListeners = new Set<IncomingCallListener>();\n\n /** \u8DEF\u7531\u7BA1\u7406\u5668 */\n private routingManager: RoutingManager;\n /** \u6D88\u606F\u5904\u7406\u5668 */\n private messageHandler: MessageHandler;\n\n /**\n * \u521B\u5EFA PeerJsWrapper \u5B9E\u4F8B\n * @param peerId \u53EF\u9009\u7684 Peer ID\uFF0C\u5982\u679C\u4E0D\u63D0\u4F9B\u5219\u81EA\u52A8\u751F\u6210 UUID\n * @param isDebug \u662F\u5426\u5F00\u542F\u8C03\u8BD5\u6A21\u5F0F\uFF0C\u5F00\u542F\u540E\u4F1A\u6253\u5370\u4E8B\u4EF6\u65E5\u5FD7\n * @param server \u53EF\u9009\u7684\u4FE1\u4EE4\u670D\u52A1\u5668\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528 PeerJS \u516C\u5171\u670D\u52A1\u5668\n * @param relayConfig \u53EF\u9009\u7684\u4E2D\u7EE7\u914D\u7F6E\n */\n constructor(peerId?: string, isDebug?: boolean, server?: ServerConfig, relayConfig?: RelayConfig) {\n this.myPeerId = peerId || generateUUID();\n this.isDebug = isDebug ?? false;\n this.serverConfig = server;\n\n const callbacks = {\n getMyPeerId: () => this.myPeerId,\n getPeerInstance: () => this.peerInstance,\n debugLog: this.debugLog.bind(this),\n };\n\n this.routingManager = new RoutingManager(callbacks, relayConfig);\n this.messageHandler = new MessageHandler({\n ...callbacks,\n waitForReady: () => this.waitForReady(),\n getSimpleHandlers: () => this.simpleHandlers,\n onRouteUpdate: (fromPeerId, message) => this.routingManager.handleRouteUpdate(fromPeerId, message),\n });\n\n this.connect();\n }\n\n private debugLog(obj: string, event: string, data?: unknown): void {\n if (this.isDebug) {\n console.log(obj, event, data);\n }\n }\n\n private connect(): void {\n if (this.isDestroyed) return;\n\n this.peerInstance = this.serverConfig\n ? new Peer(this.myPeerId, { ...this.serverConfig })\n : new Peer(this.myPeerId);\n\n this.setupPeerEventHandlers();\n }\n\n private setupPeerEventHandlers(): void {\n if (!this.peerInstance) return;\n\n this.peerInstance.on('open', (id) => {\n this.debugLog('Peer', 'open', id);\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n });\n\n this.peerInstance.on('disconnected', () => {\n this.debugLog('Peer', 'disconnected');\n this.scheduleReconnect();\n });\n\n this.peerInstance.on('error', (err) => {\n this.debugLog('Peer', 'error', { type: err.type, message: err.message });\n if (\n err.type === 'network' ||\n err.type === 'server-error' ||\n err.type === 'socket-error' ||\n err.type === 'socket-closed'\n ) {\n this.scheduleReconnect();\n }\n });\n\n this.peerInstance.on('close', () => {\n this.debugLog('Peer', 'close');\n });\n\n this.peerInstance.on('call', (mediaConnection: MediaConnection) => {\n this.handleIncomingCall(mediaConnection);\n });\n\n this.setupIncomingConnectionHandler();\n }\n\n private scheduleReconnect(): void {\n if (this.isDestroyed) return;\n if (this.reconnectTimer) return;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.reconnect();\n }, 1000);\n }\n\n private reconnect(): void {\n if (this.isDestroyed) return;\n this.debugLog('PeerJsWrapper', 'reconnect');\n\n if (this.peerInstance) {\n try {\n this.peerInstance.destroy();\n } catch {\n // ignore\n }\n this.peerInstance = null;\n }\n\n this.connect();\n }\n\n getPeerId(): string {\n return this.myPeerId;\n }\n\n whenReady(): Promise<void> {\n return this.waitForReady();\n }\n\n private waitForReady(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not initialized'));\n return;\n }\n\n if (this.peerInstance.open) {\n resolve();\n return;\n }\n\n const onOpen = () => {\n this.peerInstance?.off('open', onOpen);\n this.peerInstance?.off('error', onError);\n resolve();\n };\n\n const onError = (err: Error) => {\n this.peerInstance?.off('open', onOpen);\n this.peerInstance?.off('error', onError);\n reject(err);\n };\n\n this.peerInstance.on('open', onOpen);\n this.peerInstance.on('error', onError);\n });\n }\n\n relaySend(targetId: string, path: string, data: unknown, relayNodes: string[]): Promise<unknown> {\n if (relayNodes.length === 0) {\n return this.send(targetId, path, data);\n }\n\n const [firstRelay, ...remainingRelays] = relayNodes;\n\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'relaySend', { targetId, firstRelay, remainingRelays });\n\n this.waitForReady()\n .then(() => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = this.peerInstance.connect(firstRelay, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Relay timeout: ${firstRelay}${path}`));\n }, 30000);\n\n conn.on('open', () => {\n this.debugLog('Conn', 'open', firstRelay);\n\n const request: Request = { path, data };\n const message: RelayMessage = {\n type: 'relay-request',\n id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,\n originalTarget: targetId,\n relayPath: [],\n forwardPath: remainingRelays,\n request,\n };\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n const message = responseData as RelayMessage;\n\n if (message.type === 'relay-response') {\n clearTimeout(timeout);\n conn.close();\n\n const response = message.response;\n if (response) {\n if (response.status < 200 || response.status >= 300) {\n reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));\n } else {\n this.routingManager.recordSuccessfulNode(firstRelay);\n this.routingManager.broadcastRouteUpdate();\n resolve(response.data);\n }\n }\n } else if (message.type === 'route-update') {\n this.routingManager.handleRouteUpdate(firstRelay, message);\n }\n });\n\n conn.on('error', (err) => {\n this.debugLog('Conn', 'error', { peer: firstRelay, error: err });\n clearTimeout(timeout);\n reject(err);\n });\n\n conn.on('close', () => {\n this.debugLog('Conn', 'close', firstRelay);\n clearTimeout(timeout);\n reject(new Error('Relay connection closed'));\n });\n })\n .catch(reject);\n });\n }\n\n getRoutingTable(): Record<string, RouteEntry> {\n return this.routingManager.getRoutingTable();\n }\n\n getKnownNodes(): string[] {\n return this.routingManager.getKnownNodes();\n }\n\n send(peerId: string, path: string, data?: unknown): Promise<unknown> {\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'send', { peerId, path, data });\n\n this.waitForReady()\n .then(() => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = this.peerInstance.connect(peerId, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Request timeout: ${peerId}${path}`));\n }, 30000);\n\n const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;\n this.pendingRequests.set(requestId, { resolve, reject, timeout });\n\n conn.on('open', () => {\n this.debugLog('Conn', 'open', peerId);\n const request: Request = { path, data };\n const message: InternalMessage = {\n type: 'request',\n id: requestId,\n request,\n };\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n this.debugLog('Conn', 'data', { peer: peerId, data: responseData });\n const message = responseData as InternalMessage;\n if (message.type === 'response' && message.id === requestId) {\n const pending = this.pendingRequests.get(requestId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(requestId);\n\n const response = message.response!;\n if (response.status < 200 || response.status >= 300) {\n pending.reject(\n new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`)\n );\n } else {\n this.routingManager.recordSuccessfulNode(peerId);\n this.routingManager.broadcastRouteUpdate();\n pending.resolve(response.data);\n }\n }\n conn.close();\n }\n });\n\n conn.on('error', (err) => {\n this.debugLog('Conn', 'error', { peer: peerId, error: err });\n const pending = this.pendingRequests.get(requestId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(requestId);\n pending.reject(err as Error);\n }\n });\n\n conn.on('close', () => {\n this.debugLog('Conn', 'close', peerId);\n const pending = this.pendingRequests.get(requestId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(requestId);\n pending.reject(new Error('Connection closed'));\n }\n });\n })\n .catch((err) => {\n reject(err);\n });\n });\n }\n\n private setupIncomingConnectionHandler(): void {\n if (!this.peerInstance) return;\n\n this.peerInstance.on('connection', (conn: DataConnection) => {\n this.debugLog('Peer', 'connection', conn.peer);\n this.connections.add(conn);\n\n conn.on('open', () => {\n this.debugLog('Conn', 'open', conn.peer);\n });\n\n conn.on('data', async (data: unknown) => {\n this.debugLog('Conn', 'data', { peer: conn.peer, data });\n\n const message = data as RelayMessage | InternalMessage;\n\n if (message.type === 'request') {\n const internalMsg = message as InternalMessage;\n if (internalMsg.request) {\n try {\n const response = await this.messageHandler.handleRequest(conn.peer, internalMsg.request);\n\n const responseMessage: InternalMessage = {\n type: 'response',\n id: internalMsg.id,\n response,\n };\n\n conn.send(responseMessage);\n } catch (error) {\n const errorResponse: InternalMessage = {\n type: 'response',\n id: internalMsg.id,\n response: {\n status: 500,\n data: { error: error instanceof Error ? error.message : 'Unknown error' },\n },\n };\n\n conn.send(errorResponse);\n }\n }\n } else if (message.type === 'relay-request') {\n const relayMsg = message as RelayMessage;\n if (relayMsg.request) {\n try {\n const response = await this.messageHandler.handleRequest(conn.peer, relayMsg.request, relayMsg);\n\n const responseMessage: RelayMessage = {\n type: 'relay-response',\n id: relayMsg.id,\n originalTarget: relayMsg.originalTarget,\n relayPath: relayMsg.relayPath,\n forwardPath: [],\n response,\n };\n\n conn.send(responseMessage);\n } catch (error) {\n const errorResponse: RelayMessage = {\n type: 'relay-response',\n id: relayMsg.id,\n originalTarget: relayMsg.originalTarget,\n relayPath: relayMsg.relayPath,\n forwardPath: [],\n response: {\n status: 500,\n data: { error: error instanceof Error ? error.message : 'Unknown error' },\n },\n };\n\n conn.send(errorResponse);\n }\n }\n } else if (message.type === 'route-update') {\n this.routingManager.handleRouteUpdate(conn.peer, message as RelayMessage);\n }\n });\n\n conn.on('close', () => {\n this.debugLog('Conn', 'close', conn.peer);\n this.connections.delete(conn);\n });\n\n conn.on('error', (err) => {\n this.debugLog('Conn', 'error', { peer: conn.peer, error: err });\n this.connections.delete(conn);\n });\n });\n }\n\n call(peerId: string, options?: CallOptions): Promise<CallSession> {\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'call', { peerId, options });\n\n if (this.activeCall) {\n reject(new Error('Already in a call'));\n return;\n }\n\n this.waitForReady()\n .then(async () => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const hasVideo = options?.video ?? false;\n\n let localStream: MediaStream;\n try {\n localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: hasVideo\n });\n } catch (err) {\n reject(new Error(`Failed to get media: ${err instanceof Error ? err.message : err}`));\n return;\n }\n\n const mediaConnection = this.peerInstance.call(peerId, localStream, {\n metadata: {\n video: hasVideo,\n custom: options?.metadata\n }\n });\n\n const session = new CallSessionImpl(\n peerId,\n mediaConnection,\n hasVideo,\n this.debugLog.bind(this),\n this.cleanupCall.bind(this)\n );\n session.setLocalStream(localStream);\n\n this.setupMediaConnectionHandlers(session, mediaConnection);\n\n this.activeCall = session;\n\n const timeout = setTimeout(() => {\n if (!session.isConnected) {\n session.hangUp();\n reject(new Error('Call timeout - no answer'));\n }\n }, 30000);\n\n const onConnected = () => {\n clearTimeout(timeout);\n session.offStateChange(onConnected);\n session.offStateChange(onEnded);\n resolve(session);\n };\n\n const onEnded = (state: CallState, reason?: string) => {\n clearTimeout(timeout);\n session.offStateChange(onConnected);\n session.offStateChange(onEnded);\n if (state === 'ended') {\n reject(new Error(reason || 'Call ended before connected'));\n }\n };\n\n session.onStateChange(onConnected);\n session.onStateChange(onEnded);\n })\n .catch(reject);\n });\n }\n\n onIncomingCall(listener: IncomingCallListener): void {\n this.incomingCallListeners.add(listener);\n }\n\n offIncomingCall(listener: IncomingCallListener): void {\n this.incomingCallListeners.delete(listener);\n }\n\n getActiveCall(): CallSession | null {\n return this.activeCall;\n }\n\n private setupMediaConnectionHandlers(session: CallSessionImpl, mediaConnection: MediaConnection): void {\n mediaConnection.on('stream', (remoteStream: MediaStream) => {\n this.debugLog('MediaConnection', 'stream', { peer: mediaConnection.peer });\n session.setRemoteStream(remoteStream);\n session.setState('connected');\n });\n\n mediaConnection.on('close', () => {\n this.debugLog('MediaConnection', 'close', mediaConnection.peer);\n session.close();\n session.setState('ended', 'Connection closed');\n });\n\n mediaConnection.on('error', (err) => {\n this.debugLog('MediaConnection', 'error', { peer: mediaConnection.peer, error: err });\n session.close();\n session.setState('ended', err.message || 'Media error');\n });\n }\n\n private cleanupCall(session: CallSessionImpl): void {\n if (this.activeCall === session) {\n this.activeCall = null;\n }\n }\n\n private handleIncomingCall(mediaConnection: MediaConnection): void {\n this.debugLog('Peer', 'call', { from: mediaConnection.peer, metadata: mediaConnection.metadata });\n\n const metadata = mediaConnection.metadata as { video?: boolean; custom?: unknown } | undefined;\n const hasVideo = metadata?.video ?? false;\n\n const event: IncomingCallEvent = {\n from: mediaConnection.peer,\n hasVideo,\n metadata: metadata?.custom,\n\n answer: async () => {\n if (this.activeCall) {\n mediaConnection.close();\n throw new Error('Already in a call');\n }\n\n let localStream: MediaStream;\n try {\n localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: hasVideo\n });\n } catch (err) {\n mediaConnection.close();\n throw new Error(`Failed to get media: ${err instanceof Error ? err.message : err}`);\n }\n\n const session = new CallSessionImpl(\n mediaConnection.peer,\n mediaConnection,\n hasVideo,\n this.debugLog.bind(this),\n this.cleanupCall.bind(this)\n );\n session.setLocalStream(localStream);\n\n this.setupMediaConnectionHandlers(session, mediaConnection);\n\n this.activeCall = session;\n\n mediaConnection.answer(localStream);\n\n return session;\n },\n\n reject: () => {\n mediaConnection.close();\n this.debugLog('Call', 'rejected', mediaConnection.peer);\n }\n };\n\n this.incomingCallListeners.forEach(listener => {\n try {\n listener(event);\n } catch (err) {\n this.debugLog('IncomingCallListener', 'error', err);\n }\n });\n }\n\n registerHandler(path: string, handler: SimpleHandler): void {\n this.simpleHandlers.set(path, handler);\n }\n\n unregisterHandler(path: string): void {\n this.simpleHandlers.delete(path);\n }\n\n destroy(): void {\n this.isDestroyed = true;\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.activeCall) {\n this.activeCall.hangUp();\n this.activeCall = null;\n }\n\n this.incomingCallListeners.clear();\n\n for (const conn of this.connections.values()) {\n conn.close();\n }\n this.connections.clear();\n\n for (const pending of this.pendingRequests.values()) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Peer destroyed'));\n }\n this.pendingRequests.clear();\n this.simpleHandlers.clear();\n\n if (this.peerInstance) {\n this.peerInstance.destroy();\n this.peerInstance = null;\n }\n }\n}\n\nexport type {\n Request,\n Response,\n SimpleHandler,\n CallOptions,\n CallSession,\n CallState,\n CallStateListener,\n IncomingCallEvent,\n IncomingCallListener,\n RouteEntry,\n RelayConfig,\n RelayMessage\n};\n", "/**\n * CallSessionImpl - \u901A\u8BDD\u4F1A\u8BDD\u7684\u5185\u90E8\u5B9E\u73B0\n * \n * \u8D1F\u8D23\u7BA1\u7406\u5355\u4E2A\u8BED\u97F3/\u89C6\u9891\u901A\u8BDD\u7684\u5B8C\u6574\u751F\u547D\u5468\u671F\uFF1A\n * - \u97F3\u89C6\u9891\u6D41\u7BA1\u7406\n * - \u9759\u97F3/\u89C6\u9891\u5F00\u5173\u72B6\u6001\n * - \u901A\u8BDD\u72B6\u6001\u53D8\u5316\u901A\u77E5\n * \n * @example\n * const session = new CallSessionImpl(peerId, mediaConnection, hasVideo, debugLog, onCleanup);\n * session.toggleMute(); // \u5207\u6362\u9759\u97F3\n * session.toggleVideo(); // \u5207\u6362\u89C6\u9891\n * session.hangUp(); // \u6302\u65AD\u901A\u8BDD\n */\n\nimport type { MediaConnection } from 'peerjs';\nimport type { CallSession, CallState, CallStateListener } from './types';\n\n/**\n * \u901A\u8BDD\u4F1A\u8BDD\u5B9E\u73B0\u7C7B\n * \u5B9E\u73B0 CallSession \u63A5\u53E3\uFF0C\u63D0\u4F9B\u901A\u8BDD\u63A7\u5236\u529F\u80FD\n */\nexport class CallSessionImpl implements CallSession {\n /** \u5BF9\u7AEF\u7684 Peer ID */\n readonly peerId: string;\n /** \u662F\u5426\u5305\u542B\u89C6\u9891 */\n readonly hasVideo: boolean;\n\n /** PeerJS MediaConnection \u5B9E\u4F8B */\n private mediaConnection: MediaConnection;\n /** \u672C\u5730\u5A92\u4F53\u6D41\uFF08\u9EA6\u514B\u98CE/\u6444\u50CF\u5934\uFF09 */\n private localStream: MediaStream | null = null;\n /** \u8FDC\u7A0B\u5A92\u4F53\u6D41\uFF08\u5BF9\u65B9\u7684\u97F3\u9891/\u89C6\u9891\uFF09 */\n private remoteStream: MediaStream | null = null;\n /** \u901A\u8BDD\u72B6\u6001\u76D1\u542C\u5668\u96C6\u5408 */\n private stateListeners = new Set<CallStateListener>();\n /** \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570 */\n private debugLogFn: (obj: string, event: string, data?: unknown) => void;\n /** \u6E05\u7406\u56DE\u8C03\uFF08\u901A\u8BDD\u7ED3\u675F\u65F6\u8C03\u7528\uFF09 */\n private onCleanup: (session: CallSessionImpl) => void;\n\n /** \u5F53\u524D\u901A\u8BDD\u72B6\u6001 */\n private _state: CallState = 'connecting';\n /** \u662F\u5426\u5DF2\u9759\u97F3 */\n private isMuted = false;\n /** \u89C6\u9891\u662F\u5426\u5F00\u542F */\n private isVideoEnabled = true;\n\n /**\n * \u521B\u5EFA\u901A\u8BDD\u4F1A\u8BDD\u5B9E\u4F8B\n * @param peerId \u5BF9\u7AEF Peer ID\n * @param mediaConnection PeerJS MediaConnection \u5B9E\u4F8B\n * @param hasVideo \u662F\u5426\u5305\u542B\u89C6\u9891\n * @param debugLog \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570\n * @param onCleanup \u901A\u8BDD\u7ED3\u675F\u65F6\u7684\u6E05\u7406\u56DE\u8C03\n */\n constructor(\n peerId: string,\n mediaConnection: MediaConnection,\n hasVideo: boolean,\n debugLog: (obj: string, event: string, data?: unknown) => void,\n onCleanup: (session: CallSessionImpl) => void\n ) {\n this.peerId = peerId;\n this.mediaConnection = mediaConnection;\n this.hasVideo = hasVideo;\n this.debugLogFn = debugLog;\n this.onCleanup = onCleanup;\n }\n\n /** \u662F\u5426\u5DF2\u8FDE\u63A5 */\n get isConnected(): boolean {\n return this._state === 'connected';\n }\n\n /** \u5F53\u524D\u901A\u8BDD\u72B6\u6001 */\n get state(): CallState {\n return this._state;\n }\n\n /**\n * \u8BBE\u7F6E\u901A\u8BDD\u72B6\u6001\n * @param state \u65B0\u7684\u901A\u8BDD\u72B6\u6001\n * @param reason \u72B6\u6001\u53D8\u5316\u539F\u56E0\uFF08\u53EF\u9009\uFF09\n */\n setState(state: CallState, reason?: string): void {\n this._state = state;\n this.notifyStateChange(state, reason);\n }\n\n /** \u8BBE\u7F6E\u672C\u5730\u5A92\u4F53\u6D41 */\n setLocalStream(stream: MediaStream): void {\n this.localStream = stream;\n }\n\n /** \u8BBE\u7F6E\u8FDC\u7A0B\u5A92\u4F53\u6D41 */\n setRemoteStream(stream: MediaStream): void {\n this.remoteStream = stream;\n }\n\n /** \u83B7\u53D6\u672C\u5730\u5A92\u4F53\u6D41 */\n getLocalStream(): MediaStream | null {\n return this.localStream;\n }\n\n /** \u83B7\u53D6\u8FDC\u7A0B\u5A92\u4F53\u6D41 */\n getRemoteStream(): MediaStream | null {\n return this.remoteStream;\n }\n\n /**\n * \u5207\u6362\u9759\u97F3\u72B6\u6001\n * @returns \u5207\u6362\u540E\u7684\u9759\u97F3\u72B6\u6001\uFF08true = \u5DF2\u9759\u97F3\uFF09\n */\n toggleMute(): boolean {\n if (!this.localStream) return this.isMuted;\n\n const audioTracks = this.localStream.getAudioTracks();\n for (const track of audioTracks) {\n track.enabled = this.isMuted;\n }\n this.isMuted = !this.isMuted;\n this.debugLogFn('CallSession', 'toggleMute', this.isMuted);\n return this.isMuted;\n }\n\n /**\n * \u5207\u6362\u89C6\u9891\u5F00\u5173\uFF08\u4EC5\u89C6\u9891\u901A\u8BDD\u6709\u6548\uFF09\n * @returns \u5207\u6362\u540E\u7684\u89C6\u9891\u72B6\u6001\uFF08true = \u89C6\u9891\u5F00\u542F\uFF09\n */\n toggleVideo(): boolean {\n if (!this.hasVideo || !this.localStream) return this.isVideoEnabled;\n\n const videoTracks = this.localStream.getVideoTracks();\n for (const track of videoTracks) {\n track.enabled = !this.isVideoEnabled;\n }\n this.isVideoEnabled = !this.isVideoEnabled;\n this.debugLogFn('CallSession', 'toggleVideo', this.isVideoEnabled);\n return this.isVideoEnabled;\n }\n\n /** \u6302\u65AD\u901A\u8BDD */\n hangUp(): void {\n this.debugLogFn('CallSession', 'hangUp', this.peerId);\n this.mediaConnection.close();\n }\n\n /**\n * \u5173\u95ED\u901A\u8BDD\u4F1A\u8BDD\n * \u505C\u6B62\u672C\u5730\u6D41\uFF0C\u6E05\u7406\u8D44\u6E90\n */\n close(): void {\n if (this.localStream) {\n this.localStream.getTracks().forEach(track => track.stop());\n this.localStream = null;\n }\n this.remoteStream = null;\n this._state = 'ended';\n }\n\n /** \u6CE8\u518C\u901A\u8BDD\u72B6\u6001\u53D8\u5316\u76D1\u542C\u5668 */\n onStateChange(listener: CallStateListener): void {\n this.stateListeners.add(listener);\n }\n\n /** \u79FB\u9664\u901A\u8BDD\u72B6\u6001\u53D8\u5316\u76D1\u542C\u5668 */\n offStateChange(listener: CallStateListener): void {\n this.stateListeners.delete(listener);\n }\n\n /**\n * \u901A\u77E5\u72B6\u6001\u53D8\u5316\n * @param state \u65B0\u72B6\u6001\n * @param reason \u53D8\u5316\u539F\u56E0\uFF08\u53EF\u9009\uFF09\n */\n private notifyStateChange(state: CallState, reason?: string): void {\n this.debugLogFn('CallSession', 'stateChange', { peer: this.peerId, state, reason });\n this.stateListeners.forEach(listener => {\n try {\n listener(state, reason);\n } catch (err) {\n this.debugLogFn('CallSession', 'listenerError', err);\n }\n });\n\n // \u901A\u8BDD\u7ED3\u675F\u65F6\u6267\u884C\u6E05\u7406\n if (state === 'ended') {\n this.onCleanup(this);\n }\n }\n}\n", "/**\n * RoutingManager - \u8DEF\u7531\u7BA1\u7406\u6A21\u5757\n * \n * \u8D1F\u8D23\u4E2D\u7EE7\u901A\u4FE1\u7684\u6838\u5FC3\u529F\u80FD\uFF1A\n * - \u7EF4\u62A4\u5DF2\u77E5\u7684\u6210\u529F\u901A\u4FE1\u8282\u70B9\u5217\u8868\uFF08knownNodes\uFF09\n * - \u7EF4\u62A4\u8DEF\u7531\u8868\uFF08routingTable\uFF09\n * - \u5E7F\u64AD\u8DEF\u7531\u66F4\u65B0\u5230\u90BB\u5C45\u8282\u70B9\n * - \u5904\u7406\u6536\u5230\u7684\u8DEF\u7531\u66F4\u65B0\u6D88\u606F\n * \n * \u8DEF\u7531\u673A\u5236\uFF1A\n * 1. \u6BCF\u6B21\u6210\u529F\u901A\u4FE1\u540E\uFF0C\u8BB0\u5F55\u5BF9\u65B9\u8282\u70B9\u4E3A\u5DF2\u77E5\u8282\u70B9\n * 2. \u6210\u529F\u540E\u5E7F\u64AD\u8DEF\u7531\u66F4\u65B0\uFF0C\u544A\u77E5\u5BF9\u65B9\u81EA\u5DF1\u53EF\u8FBE\u7684\u8282\u70B9\n * 3. \u6536\u5230\u8DEF\u7531\u66F4\u65B0\u540E\uFF0C\u5408\u5E76\u5230\u672C\u5730\u8DEF\u7531\u8868\n * \n * @example\n * const routing = new RoutingManager(callbacks, { maxRelayNodes: 5 });\n * routing.recordSuccessfulNode(peerId); // \u8BB0\u5F55\u6210\u529F\u8282\u70B9\n * routing.broadcastRouteUpdate(); // \u5E7F\u64AD\u8DEF\u7531\n * const table = routing.getRoutingTable(); // \u83B7\u53D6\u8DEF\u7531\u8868\n */\n\nimport type { Peer } from 'peerjs';\nimport type { RouteEntry, RelayConfig, RelayMessage } from './types';\n\n/**\n * \u8DEF\u7531\u7BA1\u7406\u5668\u56DE\u8C03\u63A5\u53E3\n */\nexport interface RoutingCallbacks {\n /** \u83B7\u53D6\u672C\u5730 Peer ID */\n getMyPeerId(): string;\n /** \u83B7\u53D6 PeerJS \u5B9E\u4F8B */\n getPeerInstance(): Peer | null;\n /** \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570 */\n debugLog: (obj: string, event: string, data?: unknown) => void;\n}\n\n/**\n * \u8DEF\u7531\u7BA1\u7406\u5668\u7C7B\n * \u8D1F\u8D23\u7EF4\u62A4\u8DEF\u7531\u8868\u548C\u8282\u70B9\u53D1\u73B0\n */\nexport class RoutingManager {\n /** \u8DEF\u7531\u8868\uFF1Atarget -> RouteEntry */\n private routingTable = new Map<string, RouteEntry>();\n /** \u5DF2\u77E5\u7684\u6210\u529F\u901A\u4FE1\u8282\u70B9\u5217\u8868 */\n private knownNodes: string[] = [];\n /** \u4E2D\u7EE7\u914D\u7F6E */\n private relayConfig: RelayConfig;\n /** \u56DE\u8C03\u51FD\u6570\u96C6\u5408 */\n private callbacks: RoutingCallbacks;\n\n /**\n * \u521B\u5EFA\u8DEF\u7531\u7BA1\u7406\u5668\n * @param callbacks \u56DE\u8C03\u51FD\u6570\u96C6\u5408\n * @param relayConfig \u4E2D\u7EE7\u914D\u7F6E\uFF08\u53EF\u9009\uFF09\n */\n constructor(callbacks: RoutingCallbacks, relayConfig?: RelayConfig) {\n this.callbacks = callbacks;\n this.relayConfig = relayConfig ?? {};\n }\n\n /**\n * \u8BB0\u5F55\u6210\u529F\u7684\u901A\u4FE1\u8282\u70B9\n * \u5C06\u6210\u529F\u901A\u4FE1\u7684\u8282\u70B9\u6DFB\u52A0\u5230\u5DF2\u77E5\u8282\u70B9\u5217\u8868\n * @param nodeId \u8282\u70B9 ID\n */\n recordSuccessfulNode(nodeId: string): void {\n const myPeerId = this.callbacks.getMyPeerId();\n if (nodeId === myPeerId) return;\n \n if (!this.knownNodes.includes(nodeId)) {\n const maxRelayNodes = this.relayConfig.maxRelayNodes ?? 5;\n this.knownNodes.push(nodeId);\n // \u4FDD\u6301\u5217\u8868\u5728\u6700\u5927\u957F\u5EA6\u5185\n if (this.knownNodes.length > maxRelayNodes) {\n this.knownNodes.shift();\n }\n this.callbacks.debugLog('Routing', 'newNode', nodeId);\n }\n }\n\n /**\n * \u5E7F\u64AD\u8DEF\u7531\u66F4\u65B0\n * \u5411\u6240\u6709\u5DF2\u77E5\u8282\u70B9\u53D1\u9001\u8DEF\u7531\u66F4\u65B0\u6D88\u606F\uFF0C\u544A\u77E5\u5B83\u4EEC\u672C\u8282\u70B9\u53EF\u8FBE\u7684\u8282\u70B9\u5217\u8868\n */\n async broadcastRouteUpdate(): Promise<void> {\n const myPeerId = this.callbacks.getMyPeerId();\n // \u53EF\u8FBE\u8282\u70B9 = \u5DF2\u77E5\u8282\u70B9 + \u81EA\u5DF1\n const reachableNodes = [...this.knownNodes, myPeerId];\n \n for (const nodeId of this.knownNodes) {\n try {\n await this.sendRouteUpdate(nodeId, reachableNodes);\n } catch {\n // \u5FFD\u7565\u5355\u4E2A\u8282\u70B9\u7684\u5E7F\u64AD\u5931\u8D25\n }\n }\n }\n\n /**\n * \u53D1\u9001\u8DEF\u7531\u66F4\u65B0\u5230\u6307\u5B9A\u8282\u70B9\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param reachableNodes \u53EF\u8FBE\u7684\u8282\u70B9\u5217\u8868\n */\n private async sendRouteUpdate(targetId: string, reachableNodes: string[]): Promise<void> {\n const peerInstance = this.callbacks.getPeerInstance();\n const myPeerId = this.callbacks.getMyPeerId();\n \n if (!peerInstance) {\n throw new Error('Peer instance not available');\n }\n\n return new Promise((resolve, reject) => {\n const conn = peerInstance.connect(targetId, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error('Route update timeout'));\n }, 5000);\n\n conn.on('open', () => {\n const message: RelayMessage = {\n type: 'route-update',\n id: `${myPeerId}-route-${Date.now()}`,\n originalTarget: targetId,\n relayPath: [],\n forwardPath: [],\n routeUpdate: { reachableNodes },\n };\n conn.send(message);\n clearTimeout(timeout);\n conn.close();\n resolve();\n });\n\n conn.on('error', () => {\n clearTimeout(timeout);\n reject(new Error('Route update failed'));\n });\n\n conn.on('close', () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n }\n\n /**\n * \u5904\u7406\u6536\u5230\u7684\u8DEF\u7531\u66F4\u65B0\n * \u5408\u5E76\u5BF9\u7AEF\u53D1\u6765\u7684\u53EF\u8FBE\u8282\u70B9\u4FE1\u606F\u5230\u672C\u5730\u8DEF\u7531\u8868\n * @param fromPeerId \u53D1\u9001\u8DEF\u7531\u66F4\u65B0\u7684\u8282\u70B9 ID\n * @param message \u8DEF\u7531\u66F4\u65B0\u6D88\u606F\n */\n handleRouteUpdate(fromPeerId: string, message: RelayMessage): void {\n if (!message.routeUpdate) return;\n\n const myPeerId = this.callbacks.getMyPeerId();\n const { reachableNodes } = message.routeUpdate;\n const timestamp = Date.now();\n\n // \u904D\u5386\u53EF\u8FBE\u8282\u70B9\u5217\u8868\uFF0C\u66F4\u65B0\u8DEF\u7531\u8868\n for (const target of reachableNodes) {\n if (target === myPeerId) continue;\n\n const existing = this.routingTable.get(target);\n // \u8DF3\u6570 = 1 + (\u5BF9\u65B9\u5230\u76EE\u6807\u7684\u8DF3\u6570\uFF0C\u5047\u8BBE\u5BF9\u65B9\u5230\u81EA\u5DF1\u662F\u4E00\u8DF3)\n const hops = 1 + (fromPeerId === myPeerId ? 0 : 1);\n\n // \u53EA\u6709\u8DEF\u7531\u4E0D\u5B58\u5728\u3001\u8DF3\u6570\u66F4\u5C11\u3001\u6216\u66F4\u65B0\u65F6\uFF0C\u624D\u66F4\u65B0\u8DEF\u7531\u8868\n if (!existing || hops < existing.hops || timestamp > existing.timestamp) {\n this.routingTable.set(target, {\n target,\n nextHop: fromPeerId,\n hops,\n via: fromPeerId,\n timestamp,\n });\n this.callbacks.debugLog('Routing', 'update', { target, nextHop: fromPeerId, hops });\n }\n }\n }\n\n /**\n * \u83B7\u53D6\u8DEF\u7531\u8868\uFF08\u7528\u4E8E\u8C03\u8BD5\u548C\u663E\u793A\uFF09\n * @returns \u8DEF\u7531\u8868\u5BF9\u8C61\n */\n getRoutingTable(): Record<string, RouteEntry> {\n const result: Record<string, RouteEntry> = {};\n this.routingTable.forEach((entry, target) => {\n result[target] = entry;\n });\n return result;\n }\n\n /**\n * \u83B7\u53D6\u5DF2\u77E5\u8282\u70B9\u5217\u8868\n * @returns \u5DF2\u77E5\u8282\u70B9 ID \u6570\u7EC4\n */\n getKnownNodes(): string[] {\n return [...this.knownNodes];\n }\n}\n", "/**\n * MessageHandler - \u6D88\u606F\u5904\u7406\u6A21\u5757\n * \n * \u8D1F\u8D23\u5904\u7406\u63A5\u6536\u5230\u7684\u5404\u7C7B\u6D88\u606F\uFF1A\n * - \u76F4\u8FDE\u8BF7\u6C42\uFF08request\uFF09\n * - \u4E2D\u7EE7\u8BF7\u6C42\uFF08relay-request\uFF09\n * - \u8DEF\u7531\u66F4\u65B0\uFF08route-update\uFF09\n * \n * \u4E2D\u7EE7\u8BF7\u6C42\u5904\u7406\u6D41\u7A0B\uFF1A\n * 1. \u6536\u5230 relay-request \u6D88\u606F\n * 2. \u5982\u679C\u662F\u76EE\u6807\u8282\u70B9\uFF0C\u5904\u7406\u8BF7\u6C42\u5E76\u8FD4\u56DE\u54CD\u5E94\n * 3. \u5982\u679C\u4E0D\u662F\u76EE\u6807\u8282\u70B9\uFF0C\u6839\u636E forwardPath \u8F6C\u53D1\u5230\u4E0B\u4E00\u4E2A\u8282\u70B9\n * 4. \u5982\u679C forwardPath \u4E3A\u7A7A\uFF0C\u5C1D\u8BD5\u76F4\u8FDE\u5230\u76EE\u6807\u8282\u70B9\n * \n * @example\n * const handler = new MessageHandler(callbacks);\n * const response = await handler.handleRequest(from, request, relayMessage);\n */\n\nimport type { Peer } from 'peerjs';\nimport type { Request, Response, SimpleHandler, RelayMessage } from './types';\n\n/**\n * \u6D88\u606F\u5904\u7406\u5668\u56DE\u8C03\u63A5\u53E3\n */\nexport interface MessageHandlerCallbacks {\n /** \u83B7\u53D6\u672C\u5730 Peer ID */\n getMyPeerId(): string;\n /** \u83B7\u53D6 PeerJS \u5B9E\u4F8B */\n getPeerInstance(): Peer | null;\n /** \u7B49\u5F85\u8FDE\u63A5\u5C31\u7EEA */\n waitForReady(): Promise<void>;\n /** \u83B7\u53D6\u5904\u7406\u5668\u6620\u5C04\u8868 */\n getSimpleHandlers(): Map<string, SimpleHandler>;\n /** \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570 */\n debugLog: (obj: string, event: string, data?: unknown) => void;\n /** \u8DEF\u7531\u66F4\u65B0\u56DE\u8C03\uFF08\u53EF\u9009\uFF09 */\n onRouteUpdate?: (fromPeerId: string, message: RelayMessage) => void;\n}\n\n/**\n * \u6D88\u606F\u5904\u7406\u5668\u7C7B\n * \u8D1F\u8D23\u89E3\u6790\u548C\u5904\u7406\u63A5\u6536\u5230\u7684\u5404\u7C7B\u6D88\u606F\n */\nexport class MessageHandler {\n /** \u56DE\u8C03\u51FD\u6570\u96C6\u5408 */\n private callbacks: MessageHandlerCallbacks;\n\n /**\n * \u521B\u5EFA\u6D88\u606F\u5904\u7406\u5668\n * @param callbacks \u56DE\u8C03\u51FD\u6570\u96C6\u5408\n */\n constructor(callbacks: MessageHandlerCallbacks) {\n this.callbacks = callbacks;\n }\n\n /**\n * \u5904\u7406\u6536\u5230\u7684\u8BF7\u6C42\n * \u6839\u636E\u662F\u5426\u6709\u4E2D\u7EE7\u6D88\u606F\u4E0A\u4E0B\u6587\u5224\u65AD\u662F\u76F4\u8FDE\u8BF7\u6C42\u8FD8\u662F\u4E2D\u7EE7\u8BF7\u6C42\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @param relayMessage \u4E2D\u7EE7\u6D88\u606F\u4E0A\u4E0B\u6587\uFF08\u53EF\u9009\uFF0C\u6709\u5219\u4E3A\u4E2D\u7EE7\u8BF7\u6C42\uFF09\n * @returns \u54CD\u5E94\u6570\u636E\n */\n async handleRequest(from: string, request: Request, relayMessage?: RelayMessage): Promise<Response> {\n if (relayMessage) {\n return this.handleRelayRequest(from, request, relayMessage);\n }\n return this.handleDirectRequest(from, request);\n }\n\n /**\n * \u5904\u7406\u76F4\u8FDE\u8BF7\u6C42\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async handleDirectRequest(from: string, request: Request): Promise<Response> {\n const result = await this.processHandler(request.path, from, request.data);\n if (this.isErrorResponse(result)) {\n return result as Response;\n }\n return { status: 200, data: result };\n }\n\n /**\n * \u5904\u7406\u4E2D\u7EE7\u8BF7\u6C42\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @param relayMessage \u4E2D\u7EE7\u6D88\u606F\u4E0A\u4E0B\u6587\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async handleRelayRequest(from: string, request: Request, relayMessage: RelayMessage): Promise<Response> {\n const myPeerId = this.callbacks.getMyPeerId();\n const { originalTarget, relayPath, forwardPath } = relayMessage;\n\n // \u5982\u679C\u6211\u662F\u76EE\u6807\u8282\u70B9\uFF0C\u5904\u7406\u8BF7\u6C42\n if (myPeerId === originalTarget) {\n const result = await this.processHandler(request.path, from, request.data);\n if (this.isErrorResponse(result)) {\n return result as Response;\n }\n return { status: 200, data: result };\n }\n\n // \u5982\u679C\u8FD8\u6709\u4E0B\u4E00\u8DF3\uFF0C\u8F6C\u53D1\u5230\u4E0B\u4E00\u4E2A\u8282\u70B9\n if (forwardPath.length > 0) {\n const nextHop = forwardPath[0];\n const remainingPath = forwardPath.slice(1);\n \n try {\n const response = await this.forwardRelay(nextHop, {\n type: 'relay-request',\n id: relayMessage.id,\n originalTarget,\n relayPath: [...relayPath, myPeerId],\n forwardPath: remainingPath,\n request,\n });\n return response;\n } catch (err) {\n return {\n status: 500,\n data: { error: `Forward failed: ${err instanceof Error ? err.message : 'Unknown error'}` },\n };\n }\n }\n\n // \u5982\u679C\u6CA1\u6709\u66F4\u591A\u8DF3\u6570\uFF0C\u5C1D\u8BD5\u76F4\u8FDE\u5230\u76EE\u6807\u8282\u70B9\n try {\n const data = await this.forwardToTarget(originalTarget, request, relayMessage);\n return { status: 200, data };\n } catch (err) {\n return {\n status: 500,\n data: { error: `Forward to target failed: ${err instanceof Error ? err.message : 'Unknown error'}` },\n };\n }\n }\n\n /**\n * \u8C03\u7528\u6CE8\u518C\u7684\u5904\u7406\u5668\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param data \u8BF7\u6C42\u6570\u636E\n * @returns \u5904\u7406\u5668\u8FD4\u56DE\u7684\u6570\u636E\uFF0C\u6216 404 \u9519\u8BEF\n */\n async processHandler(path: string, from: string, data?: unknown): Promise<unknown> {\n const handlers = this.callbacks.getSimpleHandlers();\n const simpleHandler = handlers.get(path);\n if (simpleHandler) {\n return await simpleHandler(from, data);\n }\n // \u8FD4\u56DE 404 \u9519\u8BEF\u54CD\u5E94\u683C\u5F0F\n return { status: 404, data: { error: `Path not found: ${path}` } };\n }\n\n /**\n * \u5224\u65AD\u7ED3\u679C\u662F\u5426\u4E3A\u9519\u8BEF\u54CD\u5E94\n */\n private isErrorResponse(result: unknown): result is { status: number; data: unknown } {\n return typeof result === 'object' && result !== null && 'status' in result && 'data' in result;\n }\n\n /**\n * \u8F6C\u53D1\u4E2D\u7EE7\u8BF7\u6C42\u5230\u4E0B\u4E00\u4E2A\u8282\u70B9\n * @param nextHop \u4E0B\u4E00\u8DF3\u8282\u70B9 ID\n * @param message \u8981\u8F6C\u53D1\u7684\u6D88\u606F\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async forwardRelay(nextHop: string, message: RelayMessage): Promise<Response> {\n return new Promise((resolve, reject) => {\n this.callbacks.debugLog('MessageHandler', 'forwardRelay', { nextHop });\n\n this.callbacks.waitForReady()\n .then(() => {\n const peerInstance = this.callbacks.getPeerInstance();\n if (!peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = peerInstance.connect(nextHop, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Forward timeout: ${nextHop}`));\n }, 30000);\n\n conn.on('open', () => {\n this.callbacks.debugLog('Conn', 'open', nextHop);\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n const response = responseData as RelayMessage;\n if (response.type === 'relay-response') {\n clearTimeout(timeout);\n conn.close();\n\n if (response.response) {\n resolve(response.response);\n } else {\n reject(new Error('Invalid relay response'));\n }\n }\n });\n\n conn.on('error', (err) => {\n this.callbacks.debugLog('Conn', 'error', { peer: nextHop, error: err });\n clearTimeout(timeout);\n reject(err);\n });\n\n conn.on('close', () => {\n this.callbacks.debugLog('Conn', 'close', nextHop);\n clearTimeout(timeout);\n reject(new Error('Forward connection closed'));\n });\n })\n .catch(reject);\n });\n }\n\n /**\n * \u8F6C\u53D1\u5230\u6700\u7EC8\u76EE\u6807\u8282\u70B9\uFF08\u5F53\u6CA1\u6709\u66F4\u591A\u4E2D\u7EE7\u8282\u70B9\u65F6\u4F7F\u7528\uFF09\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @param originalMessage \u539F\u59CB\u4E2D\u7EE7\u6D88\u606F\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async forwardToTarget(targetId: string, request: Request, originalMessage: RelayMessage): Promise<unknown> {\n const myPeerId = this.callbacks.getMyPeerId();\n \n return new Promise((resolve, reject) => {\n this.callbacks.debugLog('MessageHandler', 'forwardToTarget', { targetId });\n\n this.callbacks.waitForReady()\n .then(() => {\n const peerInstance = this.callbacks.getPeerInstance();\n if (!peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = peerInstance.connect(targetId, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Forward to target timeout: ${targetId}`));\n }, 30000);\n\n conn.on('open', () => {\n this.callbacks.debugLog('Conn', 'open', targetId);\n\n const message: RelayMessage = {\n type: 'relay-request',\n id: originalMessage.id,\n originalTarget: originalMessage.originalTarget,\n relayPath: [...originalMessage.relayPath, myPeerId],\n forwardPath: [],\n request,\n };\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n const response = responseData as RelayMessage;\n if (response.type === 'relay-response') {\n clearTimeout(timeout);\n conn.close();\n\n if (response.response) {\n resolve(response.response.data);\n } else {\n reject(new Error('Invalid relay response'));\n }\n }\n });\n\n conn.on('error', (err) => {\n this.callbacks.debugLog('Conn', 'error', { peer: targetId, error: err });\n clearTimeout(timeout);\n reject(err);\n });\n\n conn.on('close', () => {\n this.callbacks.debugLog('Conn', 'close', targetId);\n clearTimeout(timeout);\n reject(new Error('Forward connection closed'));\n });\n })\n .catch(reject);\n });\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/PeerJsWrapper.ts", "../src/CallSession.ts", "../src/constants.ts", "../src/RoutingDB.ts", "../src/Router.ts", "../src/MessageHandler.ts"],
4
+ "sourcesContent": ["/**\n * PeerJsWrapper - \u5C01\u88C5 PeerJS \u4E3A\u7C7B\u4F3C HTTP \u7684 API\n * \n * \u6838\u5FC3\u529F\u80FD\uFF1A\n * 1. \u7C7B\u4F3C HTTP \u7684\u8BF7\u6C42/\u54CD\u5E94\u673A\u5236\uFF08send/relaySend\uFF09\n * 2. \u8BED\u97F3/\u89C6\u9891\u901A\u8BDD\uFF08call/onIncomingCall\uFF09\n * 3. \u4E2D\u7EE7\u8DEF\u7531\uFF08relaySend/getRoutingTable/getKnownNodes\uFF09\n * \n * @example\n * // \u57FA\u672C\u8BF7\u6C42\n * const wrapper = new PeerJsWrapper();\n * const data = await wrapper.send(peerId, '/api/hello', { name: 'world' });\n * \n * // \u4E2D\u7EE7\u8BF7\u6C42\n * await wrapper.relaySend(targetId, '/api/data', 'test', ['relayNode1', 'relayNode2']);\n * \n * // \u8BED\u97F3\u901A\u8BDD\n * const call = await wrapper.call(peerId, { video: true });\n */\n\nimport { Peer, DataConnection, MediaConnection } from 'peerjs';\nimport { CallSessionImpl } from './CallSession';\nimport { Router } from './Router';\nimport { MessageHandler } from './MessageHandler';\nimport {\n CONNECTION_TIMEOUT_MS,\n SEND_TIMEOUT_MS,\n RECONNECT_DELAY_MS,\n DEFAULT_TTL,\n} from './constants';\nimport type {\n Request,\n Response,\n SimpleHandler,\n CallOptions,\n CallSession,\n CallState,\n CallStateListener,\n IncomingCallEvent,\n IncomingCallListener,\n RouteEntry,\n RelayConfig,\n RelayMessage,\n ServerConfig\n} from './types';\n\n/** \u7248\u672C\u53F7\uFF08\u6784\u5EFA\u65F6\u6CE8\u5165\uFF09 */\ndeclare const __VERSION__: string;\nexport const VERSION = __VERSION__;\nconsole.log(`[sx-peerjs-http-util] v${VERSION}`);\n\n/**\n * \u751F\u6210 UUID v4\n */\nfunction generateUUID(): string {\n return crypto.randomUUID();\n}\n\n/**\n * \u5F85\u5904\u7406\u7684\u8BF7\u6C42\n */\ninterface PendingRequest {\n /** \u6210\u529F\u56DE\u8C03 */\n resolve: (data: unknown) => void;\n /** \u5931\u8D25\u56DE\u8C03 */\n reject: (error: Error) => void;\n /** \u8D85\u65F6\u5B9A\u65F6\u5668 */\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * \u5185\u90E8\u6D88\u606F\u683C\u5F0F\uFF08\u7528\u4E8E\u76F4\u8FDE\u8BF7\u6C42\uFF09\n */\ninterface InternalMessage {\n /** \u6D88\u606F\u7C7B\u578B */\n type: 'request' | 'response';\n /** \u6D88\u606F ID\uFF0C\u7528\u4E8E\u5339\u914D\u8BF7\u6C42\u548C\u54CD\u5E94 */\n id: string;\n /** \u8BF7\u6C42\u6570\u636E */\n request?: Request;\n /** \u54CD\u5E94\u6570\u636E */\n response?: Response;\n}\n\n/**\n * PeerJsWrapper \u4E3B\u7C7B\n * \u5C01\u88C5 PeerJS\uFF0C\u63D0\u4F9B\u7C7B\u4F3C HTTP \u7684 API\n */\nexport class PeerJsWrapper {\n /** \u672C\u5730 Peer ID */\n private myPeerId: string;\n /** PeerJS \u5B9E\u4F8B */\n private peerInstance: Peer | null = null;\n /** \u5F53\u524D\u6D3B\u8DC3\u7684\u4F20\u5165\u8FDE\u63A5\u96C6\u5408 */\n private connections = new Set<DataConnection>();\n /** \u5F85\u5904\u7406\u7684\u8BF7\u6C42\u6620\u5C04\u8868\uFF08\u7528\u4E8E\u8BF7\u6C42-\u54CD\u5E94\u5339\u914D\uFF09 */\n private pendingRequests = new Map<string, PendingRequest>();\n /** \u8DEF\u5F84\u5904\u7406\u5668\u6620\u5C04\u8868 */\n private simpleHandlers = new Map<string, SimpleHandler>();\n /** \u91CD\u8FDE\u5B9A\u65F6\u5668 */\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n /** \u662F\u5426\u5DF2\u9500\u6BC1 */\n private isDestroyed = false;\n /** \u662F\u5426\u5F00\u542F\u8C03\u8BD5\u6A21\u5F0F */\n private isDebug: boolean;\n /** \u670D\u52A1\u5668\u914D\u7F6E */\n private serverConfig?: ServerConfig;\n /** \u5F53\u524D\u6D3B\u8DC3\u7684\u901A\u8BDD */\n private activeCall: CallSessionImpl | null = null;\n /** \u6765\u7535\u76D1\u542C\u5668\u96C6\u5408 */\n private incomingCallListeners = new Set<IncomingCallListener>();\n\n /** \u8DEF\u7531\u7BA1\u7406\u5668 */\n private router: Router;\n /** \u6D88\u606F\u5904\u7406\u5668 */\n private messageHandler: MessageHandler;\n\n /**\n * \u521B\u5EFA PeerJsWrapper \u5B9E\u4F8B\n * @param peerId \u53EF\u9009\u7684 Peer ID\uFF0C\u5982\u679C\u4E0D\u63D0\u4F9B\u5219\u81EA\u52A8\u751F\u6210 UUID\n * @param isDebug \u662F\u5426\u5F00\u542F\u8C03\u8BD5\u6A21\u5F0F\uFF0C\u5F00\u542F\u540E\u4F1A\u6253\u5370\u4E8B\u4EF6\u65E5\u5FD7\n * @param server \u53EF\u9009\u7684\u4FE1\u4EE4\u670D\u52A1\u5668\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528 PeerJS \u516C\u5171\u670D\u52A1\u5668\n * @param relayConfig \u53EF\u9009\u7684\u4E2D\u7EE7\u914D\u7F6E\n */\n constructor(peerId?: string, isDebug?: boolean, server?: ServerConfig, relayConfig?: RelayConfig) {\n this.myPeerId = peerId || generateUUID();\n this.isDebug = isDebug ?? false;\n this.serverConfig = server;\n\n const callbacks = {\n getMyPeerId: () => this.myPeerId,\n getPeerInstance: () => this.peerInstance,\n debugLog: this.debugLog.bind(this),\n sendRelayMessage: (targetId: string, message: RelayMessage) => this.sendRelayMessage(targetId, message),\n };\n\n this.router = new Router(callbacks, relayConfig);\n this.router.init();\n this.messageHandler = new MessageHandler({\n ...callbacks,\n waitForReady: () => this.waitForReady(),\n getSimpleHandlers: () => this.simpleHandlers,\n onRouteUpdate: (fromPeerId, message) => this.router.handleRouteUpdate(fromPeerId, message),\n });\n\n this.connect();\n }\n\n /**\n * \u521B\u5EFA\u5B9E\u4F8B\u5E76\u7B49\u5F85\u5C31\u7EEA\uFF08\u8BED\u6CD5\u7CD6\uFF09\n * @param peerId \u53EF\u9009\u7684 Peer ID\n * @param isDebug \u662F\u5426\u5F00\u542F\u8C03\u8BD5\u6A21\u5F0F\n * @param server \u53EF\u9009\u7684\u4FE1\u4EE4\u670D\u52A1\u5668\u914D\u7F6E\n * @param relayConfig \u53EF\u9009\u7684\u4E2D\u7EE7\u914D\u7F6E\n * @returns Promise<PeerJsWrapper>\n */\n static async create(\n peerId?: string,\n isDebug?: boolean,\n server?: ServerConfig,\n relayConfig?: RelayConfig\n ): Promise<PeerJsWrapper> {\n const wrapper = new PeerJsWrapper(peerId, isDebug, server, relayConfig);\n await wrapper.whenReady();\n return wrapper;\n }\n\n private debugLog(obj: string, event: string, data?: unknown): void {\n if (this.isDebug) {\n console.log(obj, event, data);\n }\n }\n\n private connect(): void {\n if (this.isDestroyed) return;\n\n this.peerInstance = this.serverConfig\n ? new Peer(this.myPeerId, { ...this.serverConfig })\n : new Peer(this.myPeerId);\n\n this.setupPeerEventHandlers();\n }\n\n private setupPeerEventHandlers(): void {\n if (!this.peerInstance) return;\n\n this.peerInstance.on('open', (id) => {\n this.debugLog('Peer', 'open', id);\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n });\n\n this.peerInstance.on('disconnected', () => {\n this.debugLog('Peer', 'disconnected');\n this.scheduleReconnect();\n });\n\n this.peerInstance.on('error', (err) => {\n this.debugLog('Peer', 'error', { type: err.type, message: err.message });\n if (\n err.type === 'network' ||\n err.type === 'server-error' ||\n err.type === 'socket-error' ||\n err.type === 'socket-closed'\n ) {\n this.scheduleReconnect();\n }\n });\n\n this.peerInstance.on('close', () => {\n this.debugLog('Peer', 'close');\n });\n\n this.peerInstance.on('call', (mediaConnection: MediaConnection) => {\n this.handleIncomingCall(mediaConnection);\n });\n\n this.setupIncomingConnectionHandler();\n }\n\n private scheduleReconnect(): void {\n if (this.isDestroyed) return;\n if (this.reconnectTimer) return;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.reconnect();\n }, RECONNECT_DELAY_MS);\n }\n\n private reconnect(): void {\n if (this.isDestroyed) return;\n this.debugLog('PeerJsWrapper', 'reconnect');\n\n if (this.peerInstance) {\n try {\n this.peerInstance.destroy();\n } catch {\n // ignore\n }\n this.peerInstance = null;\n }\n\n this.connect();\n }\n\n getPeerId(): string {\n return this.myPeerId;\n }\n\n private whenReady(): Promise<void> {\n return this.waitForReady();\n }\n\n private waitForReady(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not initialized'));\n return;\n }\n\n if (this.peerInstance.open) {\n resolve();\n return;\n }\n\n const onOpen = () => {\n this.peerInstance?.off('open', onOpen);\n this.peerInstance?.off('error', onError);\n resolve();\n };\n\n const onError = (err: Error) => {\n this.peerInstance?.off('open', onOpen);\n this.peerInstance?.off('error', onError);\n reject(err);\n };\n\n this.peerInstance.on('open', onOpen);\n this.peerInstance.on('error', onError);\n });\n }\n\n getRoutingTable(): Record<string, RouteEntry> {\n return this.router.getRoutingTable();\n }\n\n getKnownNodes(): string[] {\n return this.router.getKnownNodes();\n }\n\n /**\n * \u53D1\u9001\u4E2D\u7EE7\u6D88\u606F\u7684\u8F85\u52A9\u65B9\u6CD5\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param message \u4E2D\u7EE7\u6D88\u606F\n */\n private sendRelayMessage(targetId: string, message: RelayMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = this.peerInstance.connect(targetId, { reliable: true });\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Send to ${targetId} timeout`));\n }, SEND_TIMEOUT_MS);\n\n conn.on('open', () => {\n conn.send(message);\n clearTimeout(timeout);\n conn.close();\n resolve();\n });\n\n conn.on('error', () => {\n clearTimeout(timeout);\n reject(new Error(`Send to ${targetId} failed`));\n });\n\n conn.on('close', () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n }\n\n /**\n * \u5C1D\u8BD5\u76F4\u8FDE\u76EE\u6807\u8282\u70B9\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @param requestId \u8BF7\u6C42 ID\n * @returns Promise<unknown> - \u54CD\u5E94\u6570\u636E\n */\n private tryDirectConnect(targetId: string, path: string, data: unknown, requestId: string): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const startTime = Date.now();\n const conn = this.peerInstance.connect(targetId, { reliable: true });\n let resolved = false;\n\n const timeout = setTimeout(() => {\n if (!resolved) {\n resolved = true;\n conn.close();\n reject(new Error(`Request timeout: ${targetId}${path}`));\n }\n }, CONNECTION_TIMEOUT_MS);\n\n conn.on('open', () => {\n const latency = Date.now() - startTime;\n this.debugLog('Conn', 'open', { peer: targetId, latency });\n\n const request: Request = { path, data };\n const message: InternalMessage = {\n type: 'request',\n id: requestId,\n request,\n };\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n this.debugLog('Conn', 'data', { peer: targetId, data: responseData });\n const message = responseData as InternalMessage;\n if (message.type === 'response' && message.id === requestId) {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n\n const response = message.response!;\n if (response.status < 200 || response.status >= 300) {\n conn.close();\n reject(new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`));\n } else {\n const latency = Date.now() - startTime;\n this.router.recordDirectNode(targetId, latency);\n this.router.broadcastRouteUpdate();\n resolve(response.data);\n }\n }\n }\n });\n\n conn.on('error', (err) => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n this.debugLog('Conn', 'error', { peer: targetId, error: err });\n reject(err);\n }\n });\n\n conn.on('close', () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n this.debugLog('Conn', 'close', targetId);\n reject(new Error('Connection closed'));\n }\n });\n });\n }\n\n /**\n * \u901A\u8FC7\u4E2D\u7EE7\u8282\u70B9\u8F6C\u53D1\u8BF7\u6C42\n * @param nextHopId \u4E0B\u4E00\u8DF3\u8282\u70B9 ID\n * @param targetId \u539F\u59CB\u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @returns Promise<unknown> - \u54CD\u5E94\u6570\u636E\n */\n private relayVia(nextHopId: string, targetId: string, path: string, data: unknown): Promise<unknown> {\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'relayVia', { targetId, nextHop: nextHopId });\n\n this.waitForReady()\n .then(() => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = this.peerInstance.connect(nextHopId, { reliable: true });\n const startTime = Date.now();\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Relay timeout: ${nextHopId}${path}`));\n }, CONNECTION_TIMEOUT_MS);\n\n conn.on('open', () => {\n this.debugLog('Conn', 'open', nextHopId);\n\n const request: Request = { path, data };\n const message: RelayMessage = {\n type: 'relay-request',\n id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,\n originalTarget: targetId,\n relayPath: [this.myPeerId],\n forwardPath: [],\n ttl: DEFAULT_TTL,\n request,\n };\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n const message = responseData as RelayMessage;\n\n if (message.type === 'relay-response') {\n clearTimeout(timeout);\n conn.close();\n\n const response = message.response;\n if (response) {\n if (response.status < 200 || response.status >= 300) {\n reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));\n } else {\n const latency = Date.now() - startTime;\n this.router.recordDirectNode(nextHopId, latency);\n this.router.broadcastRouteUpdate();\n resolve(response.data);\n }\n }\n } else if (message.type === 'route-update') {\n this.router.handleRouteUpdate(nextHopId, message);\n } else if (message.type === 'route-query') {\n this.router.handleRouteQuery(nextHopId, message);\n } else if (message.type === 'route-response') {\n this.router.handleRouteResponse(nextHopId, message);\n }\n });\n\n conn.on('error', (err) => {\n this.debugLog('Conn', 'error', { peer: nextHopId, error: err });\n clearTimeout(timeout);\n reject(err);\n });\n\n conn.on('close', () => {\n this.debugLog('Conn', 'close', nextHopId);\n clearTimeout(timeout);\n reject(new Error('Relay connection closed'));\n });\n })\n .catch(reject);\n });\n }\n\n /**\n * \u81EA\u52A8\u8DEF\u7531\u53D1\u9001\n * \n * 1. \u67E5\u8DEF\u7531\u8868 \u2192 \u6709\u8DEF\u7531 \u2192 \u5C1D\u8BD5\u4E2D\u7EE7 \u2192 \u5168\u90E8\u5931\u8D25 \u2192 \u964D\u7EA7\u76F4\u8FDE \u2192 \u5931\u8D25 \u2192 \u7ED3\u675F\n * 2. \u8DEF\u7531\u8868\u65E0\u76EE\u6807 \u2192 \u76F4\u8FDE \u2192 \u5931\u8D25 \u2192 \u7ED3\u675F\n * @param peerId \u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @returns Promise<unknown> - \u54CD\u5E94\u6570\u636E\n */\n send(peerId: string, path: string, data?: unknown): Promise<unknown> {\n const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'send', { peerId, path, data });\n\n const nextHops = this.router.getNextHopsToTarget(peerId);\n\n if (nextHops.length > 0) {\n this.tryRelayChain(peerId, path, data, nextHops, 0)\n .then(resolve)\n .catch((relayErr) => {\n this.debugLog('PeerJsWrapper', 'relayFailed', { peerId, error: relayErr.message });\n this.fallbackToDirect(peerId, path, data, requestId)\n .then(resolve)\n .catch(reject);\n });\n } else {\n this.tryDirectConnect(peerId, path, data, requestId)\n .then(resolve)\n .catch((directErr) => {\n this.debugLog('PeerJsWrapper', 'directFailed', { peerId, error: directErr.message });\n this.handleSendError(peerId, directErr)\n .then(resolve)\n .catch(reject);\n });\n }\n });\n }\n\n /**\n * \u5904\u7406\u53D1\u9001\u9519\u8BEF\n * @param peerId \u76EE\u6807\u8282\u70B9 ID\n * @param error \u9519\u8BEF\u5BF9\u8C61\n * @returns Promise\n */\n private async handleSendError(peerId: string, error: Error): Promise<unknown> {\n const errorMsg = error.message;\n const isHttpError = errorMsg.includes('Request failed:') || errorMsg.includes('404') || errorMsg.includes('500');\n const isConnectionError = errorMsg.includes('timeout') || errorMsg.includes('Connection closed') || errorMsg.includes('Peer instance not available');\n\n if (isHttpError) {\n throw error;\n }\n\n if (!isConnectionError) {\n throw error;\n }\n\n this.router.removeRoute(peerId);\n this.debugLog('PeerJsWrapper', 'routeRemoved', peerId);\n\n if (!this.router.isRoutingTableEmpty()) {\n return this.performRouteDiscovery(peerId, '', undefined, '');\n }\n\n throw new Error(`Cannot reach ${peerId}: no route found and routing table is empty`);\n }\n\n /**\n * \u964D\u7EA7\u5230\u76F4\u8FDE\u5C1D\u8BD5\n * @param peerId \u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @param requestId \u8BF7\u6C42 ID\n * @returns Promise\n */\n private async fallbackToDirect(peerId: string, path: string, data: unknown, requestId: string): Promise<unknown> {\n this.debugLog('PeerJsWrapper', 'fallbackToDirect', peerId);\n\n return this.tryDirectConnect(peerId, path, data, requestId)\n .catch((directErr) => {\n this.debugLog('PeerJsWrapper', 'directFailed', { peerId, error: directErr.message });\n this.handleSendError(peerId, directErr);\n });\n }\n\n /**\n * \u5C1D\u8BD5\u901A\u8FC7\u4E2D\u7EE7\u94FE\u8F6C\u53D1\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @param nextHops \u4E0B\u4E00\u8DF3\u5217\u8868\n * @param index \u5F53\u524D\u5C1D\u8BD5\u7684\u4E0B\u4E00\u8DF3\u7D22\u5F15\n * @returns Promise<unknown>\n */\n private tryRelayChain(targetId: string, path: string, data: unknown, nextHops: { nodeId: string; latency: number }[], index: number): Promise<unknown> {\n if (index >= nextHops.length) {\n return Promise.reject(new Error('All relay nodes failed'));\n }\n\n const nextHop = nextHops[index];\n this.debugLog('PeerJsWrapper', 'tryRelay', { targetId, nextHop: nextHop.nodeId, latency: nextHop.latency });\n\n return this.relayVia(nextHop.nodeId, targetId, path, data).catch(() => {\n return this.tryRelayChain(targetId, path, data, nextHops, index + 1);\n });\n }\n\n /**\n * \u6267\u884C\u8DEF\u7531\u53D1\u73B0\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @param requestId \u8BF7\u6C42 ID\n * @returns Promise<unknown>\n */\n private async performRouteDiscovery(targetId: string, path: string, data: unknown, requestId: string): Promise<unknown> {\n this.debugLog('PeerJsWrapper', 'routeDiscovery', { targetId });\n\n const routeEntry = await this.router.discoverRoute(targetId);\n\n if (!routeEntry || routeEntry.nextHops.length === 0) {\n throw new Error(`Cannot reach ${targetId}: no route found`);\n }\n\n this.debugLog('PeerJsWrapper', 'routeFound', { targetId, nextHops: routeEntry.nextHops });\n\n return this.tryRelayChain(targetId, path, data, routeEntry.nextHops, 0);\n }\n\n /**\n * \u4E2D\u7EE7\u53D1\u9001\uFF08\u5185\u90E8\u65B9\u6CD5\uFF0C\u4E0D\u5BF9\u5916\u66B4\u9732\uFF09\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param data \u8BF7\u6C42\u6570\u636E\n * @param relayNodes \u624B\u52A8\u6307\u5B9A\u7684\u4E2D\u7EE7\u8282\u70B9\uFF08\u53EF\u9009\uFF0C\u4E0D\u6307\u5B9A\u5219\u81EA\u52A8\u8DEF\u7531\uFF09\n * @returns Promise<unknown>\n */\n private relaySend(targetId: string, path: string, data: unknown, relayNodes?: string[]): Promise<unknown> {\n if (!relayNodes || relayNodes.length === 0) {\n return this.send(targetId, path, data);\n }\n\n const [firstRelay, ...remainingRelays] = relayNodes;\n\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'relaySend', { targetId, firstRelay, remainingRelays });\n\n this.waitForReady()\n .then(() => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = this.peerInstance.connect(firstRelay, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Relay timeout: ${firstRelay}${path}`));\n }, CONNECTION_TIMEOUT_MS);\n\n conn.on('open', () => {\n this.debugLog('Conn', 'open', firstRelay);\n\n const request: Request = { path, data };\n const message: RelayMessage = {\n type: 'relay-request',\n id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,\n originalTarget: targetId,\n relayPath: [],\n forwardPath: remainingRelays,\n ttl: DEFAULT_TTL,\n request,\n };\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n const message = responseData as RelayMessage;\n\n if (message.type === 'relay-response') {\n clearTimeout(timeout);\n conn.close();\n\n const response = message.response;\n if (response) {\n if (response.status < 200 || response.status >= 300) {\n reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));\n } else {\n this.router.recordSuccessfulNode(firstRelay);\n this.router.broadcastRouteUpdate();\n resolve(response.data);\n }\n }\n } else if (message.type === 'route-update') {\n this.router.handleRouteUpdate(firstRelay, message);\n }\n });\n\n conn.on('error', (err) => {\n this.debugLog('Conn', 'error', { peer: firstRelay, error: err });\n clearTimeout(timeout);\n reject(err);\n });\n\n conn.on('close', () => {\n this.debugLog('Conn', 'close', firstRelay);\n clearTimeout(timeout);\n reject(new Error('Relay connection closed'));\n });\n })\n .catch(reject);\n });\n }\n\n private setupIncomingConnectionHandler(): void {\n if (!this.peerInstance) return;\n\n this.peerInstance.on('connection', (conn: DataConnection) => {\n this.debugLog('Peer', 'connection', conn.peer);\n this.connections.add(conn);\n\n conn.on('open', () => {\n this.debugLog('Conn', 'open', conn.peer);\n });\n\n conn.on('data', async (data: unknown) => {\n this.debugLog('Conn', 'data', { peer: conn.peer, data });\n\n const message = data as RelayMessage | InternalMessage;\n\n if (message.type === 'request') {\n const internalMsg = message as InternalMessage;\n if (internalMsg.request) {\n try {\n const response = await this.messageHandler.handleRequest(conn.peer, internalMsg.request);\n\n const responseMessage: InternalMessage = {\n type: 'response',\n id: internalMsg.id,\n response,\n };\n\n conn.send(responseMessage);\n } catch (error) {\n const errorResponse: InternalMessage = {\n type: 'response',\n id: internalMsg.id,\n response: {\n status: 500,\n data: { error: error instanceof Error ? error.message : 'Unknown error' },\n },\n };\n\n conn.send(errorResponse);\n }\n }\n } else if (message.type === 'relay-request') {\n const relayMsg = message as RelayMessage;\n if (relayMsg.request) {\n try {\n const response = await this.messageHandler.handleRequest(conn.peer, relayMsg.request, relayMsg);\n\n const responseMessage: RelayMessage = {\n type: 'relay-response',\n id: relayMsg.id,\n originalTarget: relayMsg.originalTarget,\n relayPath: relayMsg.relayPath,\n forwardPath: [],\n response,\n };\n\n conn.send(responseMessage);\n } catch (error) {\n const errorResponse: RelayMessage = {\n type: 'relay-response',\n id: relayMsg.id,\n originalTarget: relayMsg.originalTarget,\n relayPath: relayMsg.relayPath,\n forwardPath: [],\n response: {\n status: 500,\n data: { error: error instanceof Error ? error.message : 'Unknown error' },\n },\n };\n\n conn.send(errorResponse);\n }\n }\n } else if (message.type === 'route-update') {\n this.router.handleRouteUpdate(conn.peer, message as RelayMessage);\n }\n });\n\n conn.on('close', () => {\n this.debugLog('Conn', 'close', conn.peer);\n this.connections.delete(conn);\n });\n\n conn.on('error', (err) => {\n this.debugLog('Conn', 'error', { peer: conn.peer, error: err });\n this.connections.delete(conn);\n });\n });\n }\n\n call(peerId: string, options?: CallOptions): Promise<CallSession> {\n return new Promise((resolve, reject) => {\n this.debugLog('PeerJsWrapper', 'call', { peerId, options });\n\n if (this.activeCall) {\n reject(new Error('Already in a call'));\n return;\n }\n\n this.waitForReady()\n .then(async () => {\n if (!this.peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const hasVideo = options?.video ?? false;\n\n let localStream: MediaStream;\n try {\n localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: hasVideo\n });\n } catch (err) {\n reject(new Error(`Failed to get media: ${err instanceof Error ? err.message : err}`));\n return;\n }\n\n const mediaConnection = this.peerInstance.call(peerId, localStream, {\n metadata: {\n video: hasVideo,\n custom: options?.metadata\n }\n });\n\n const session = new CallSessionImpl(\n peerId,\n mediaConnection,\n hasVideo,\n this.debugLog.bind(this),\n this.cleanupCall.bind(this)\n );\n session.setLocalStream(localStream);\n\n this.setupMediaConnectionHandlers(session, mediaConnection);\n\n this.activeCall = session;\n\n const timeout = setTimeout(() => {\n if (!session.isConnected) {\n session.hangUp();\n reject(new Error('Call timeout - no answer'));\n }\n }, 30000);\n\n const onConnected = () => {\n clearTimeout(timeout);\n session.offStateChange(onConnected);\n session.offStateChange(onEnded);\n resolve(session);\n };\n\n const onEnded = (state: CallState, reason?: string) => {\n clearTimeout(timeout);\n session.offStateChange(onConnected);\n session.offStateChange(onEnded);\n if (state === 'ended') {\n reject(new Error(reason || 'Call ended before connected'));\n }\n };\n\n session.onStateChange(onConnected);\n session.onStateChange(onEnded);\n })\n .catch(reject);\n });\n }\n\n onIncomingCall(listener: IncomingCallListener): void {\n this.incomingCallListeners.add(listener);\n }\n\n offIncomingCall(listener: IncomingCallListener): void {\n this.incomingCallListeners.delete(listener);\n }\n\n getActiveCall(): CallSession | null {\n return this.activeCall;\n }\n\n private setupMediaConnectionHandlers(session: CallSessionImpl, mediaConnection: MediaConnection): void {\n mediaConnection.on('stream', (remoteStream: MediaStream) => {\n this.debugLog('MediaConnection', 'stream', { peer: mediaConnection.peer });\n session.setRemoteStream(remoteStream);\n session.setState('connected');\n });\n\n mediaConnection.on('close', () => {\n this.debugLog('MediaConnection', 'close', mediaConnection.peer);\n session.close();\n session.setState('ended', 'Connection closed');\n });\n\n mediaConnection.on('error', (err) => {\n this.debugLog('MediaConnection', 'error', { peer: mediaConnection.peer, error: err });\n session.close();\n session.setState('ended', err.message || 'Media error');\n });\n }\n\n private cleanupCall(session: CallSessionImpl): void {\n if (this.activeCall === session) {\n this.activeCall = null;\n }\n }\n\n private handleIncomingCall(mediaConnection: MediaConnection): void {\n this.debugLog('Peer', 'call', { from: mediaConnection.peer, metadata: mediaConnection.metadata });\n\n const metadata = mediaConnection.metadata as { video?: boolean; custom?: unknown } | undefined;\n const hasVideo = metadata?.video ?? false;\n\n const event: IncomingCallEvent = {\n from: mediaConnection.peer,\n hasVideo,\n metadata: metadata?.custom,\n\n answer: async () => {\n if (this.activeCall) {\n mediaConnection.close();\n throw new Error('Already in a call');\n }\n\n let localStream: MediaStream;\n try {\n localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: hasVideo\n });\n } catch (err) {\n mediaConnection.close();\n throw new Error(`Failed to get media: ${err instanceof Error ? err.message : err}`);\n }\n\n const session = new CallSessionImpl(\n mediaConnection.peer,\n mediaConnection,\n hasVideo,\n this.debugLog.bind(this),\n this.cleanupCall.bind(this)\n );\n session.setLocalStream(localStream);\n\n this.setupMediaConnectionHandlers(session, mediaConnection);\n\n this.activeCall = session;\n\n mediaConnection.answer(localStream);\n\n return session;\n },\n\n reject: () => {\n mediaConnection.close();\n this.debugLog('Call', 'rejected', mediaConnection.peer);\n }\n };\n\n this.incomingCallListeners.forEach(listener => {\n try {\n listener(event);\n } catch (err) {\n this.debugLog('IncomingCallListener', 'error', err);\n }\n });\n }\n\n registerHandler(path: string, handler: SimpleHandler): void {\n this.simpleHandlers.set(path, handler);\n }\n\n unregisterHandler(path: string): void {\n this.simpleHandlers.delete(path);\n }\n\n destroy(): void {\n this.isDestroyed = true;\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.activeCall) {\n this.activeCall.hangUp();\n this.activeCall = null;\n }\n\n this.incomingCallListeners.clear();\n\n for (const conn of this.connections.values()) {\n conn.close();\n }\n this.connections.clear();\n\n for (const pending of this.pendingRequests.values()) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Peer destroyed'));\n }\n this.pendingRequests.clear();\n this.simpleHandlers.clear();\n\n if (this.peerInstance) {\n this.peerInstance.destroy();\n this.peerInstance = null;\n }\n\n if (this.router) {\n this.router.persist();\n this.router.destroy();\n }\n }\n}\n\nexport type {\n Request,\n Response,\n SimpleHandler,\n CallOptions,\n CallSession,\n CallState,\n CallStateListener,\n IncomingCallEvent,\n IncomingCallListener,\n RouteEntry,\n RelayConfig,\n RelayMessage\n};\n", "/**\n * CallSessionImpl - \u901A\u8BDD\u4F1A\u8BDD\u7684\u5185\u90E8\u5B9E\u73B0\n * \n * \u8D1F\u8D23\u7BA1\u7406\u5355\u4E2A\u8BED\u97F3/\u89C6\u9891\u901A\u8BDD\u7684\u5B8C\u6574\u751F\u547D\u5468\u671F\uFF1A\n * - \u97F3\u89C6\u9891\u6D41\u7BA1\u7406\n * - \u9759\u97F3/\u89C6\u9891\u5F00\u5173\u72B6\u6001\n * - \u901A\u8BDD\u72B6\u6001\u53D8\u5316\u901A\u77E5\n * \n * @example\n * const session = new CallSessionImpl(peerId, mediaConnection, hasVideo, debugLog, onCleanup);\n * session.toggleMute(); // \u5207\u6362\u9759\u97F3\n * session.toggleVideo(); // \u5207\u6362\u89C6\u9891\n * session.hangUp(); // \u6302\u65AD\u901A\u8BDD\n */\n\nimport type { MediaConnection } from 'peerjs';\nimport type { CallSession, CallState, CallStateListener } from './types';\n\n/**\n * \u901A\u8BDD\u4F1A\u8BDD\u5B9E\u73B0\u7C7B\n * \u5B9E\u73B0 CallSession \u63A5\u53E3\uFF0C\u63D0\u4F9B\u901A\u8BDD\u63A7\u5236\u529F\u80FD\n */\nexport class CallSessionImpl implements CallSession {\n /** \u5BF9\u7AEF\u7684 Peer ID */\n readonly peerId: string;\n /** \u662F\u5426\u5305\u542B\u89C6\u9891 */\n readonly hasVideo: boolean;\n\n /** PeerJS MediaConnection \u5B9E\u4F8B */\n private mediaConnection: MediaConnection;\n /** \u672C\u5730\u5A92\u4F53\u6D41\uFF08\u9EA6\u514B\u98CE/\u6444\u50CF\u5934\uFF09 */\n private localStream: MediaStream | null = null;\n /** \u8FDC\u7A0B\u5A92\u4F53\u6D41\uFF08\u5BF9\u65B9\u7684\u97F3\u9891/\u89C6\u9891\uFF09 */\n private remoteStream: MediaStream | null = null;\n /** \u901A\u8BDD\u72B6\u6001\u76D1\u542C\u5668\u96C6\u5408 */\n private stateListeners = new Set<CallStateListener>();\n /** \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570 */\n private debugLogFn: (obj: string, event: string, data?: unknown) => void;\n /** \u6E05\u7406\u56DE\u8C03\uFF08\u901A\u8BDD\u7ED3\u675F\u65F6\u8C03\u7528\uFF09 */\n private onCleanup: (session: CallSessionImpl) => void;\n\n /** \u5F53\u524D\u901A\u8BDD\u72B6\u6001 */\n private _state: CallState = 'connecting';\n /** \u662F\u5426\u5DF2\u9759\u97F3 */\n private isMuted = false;\n /** \u89C6\u9891\u662F\u5426\u5F00\u542F */\n private isVideoEnabled = true;\n\n /**\n * \u521B\u5EFA\u901A\u8BDD\u4F1A\u8BDD\u5B9E\u4F8B\n * @param peerId \u5BF9\u7AEF Peer ID\n * @param mediaConnection PeerJS MediaConnection \u5B9E\u4F8B\n * @param hasVideo \u662F\u5426\u5305\u542B\u89C6\u9891\n * @param debugLog \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570\n * @param onCleanup \u901A\u8BDD\u7ED3\u675F\u65F6\u7684\u6E05\u7406\u56DE\u8C03\n */\n constructor(\n peerId: string,\n mediaConnection: MediaConnection,\n hasVideo: boolean,\n debugLog: (obj: string, event: string, data?: unknown) => void,\n onCleanup: (session: CallSessionImpl) => void\n ) {\n this.peerId = peerId;\n this.mediaConnection = mediaConnection;\n this.hasVideo = hasVideo;\n this.debugLogFn = debugLog;\n this.onCleanup = onCleanup;\n }\n\n /** \u662F\u5426\u5DF2\u8FDE\u63A5 */\n get isConnected(): boolean {\n return this._state === 'connected';\n }\n\n /** \u5F53\u524D\u901A\u8BDD\u72B6\u6001 */\n get state(): CallState {\n return this._state;\n }\n\n /**\n * \u8BBE\u7F6E\u901A\u8BDD\u72B6\u6001\n * @param state \u65B0\u7684\u901A\u8BDD\u72B6\u6001\n * @param reason \u72B6\u6001\u53D8\u5316\u539F\u56E0\uFF08\u53EF\u9009\uFF09\n */\n setState(state: CallState, reason?: string): void {\n this._state = state;\n this.notifyStateChange(state, reason);\n }\n\n /** \u8BBE\u7F6E\u672C\u5730\u5A92\u4F53\u6D41 */\n setLocalStream(stream: MediaStream): void {\n this.localStream = stream;\n }\n\n /** \u8BBE\u7F6E\u8FDC\u7A0B\u5A92\u4F53\u6D41 */\n setRemoteStream(stream: MediaStream): void {\n this.remoteStream = stream;\n }\n\n /** \u83B7\u53D6\u672C\u5730\u5A92\u4F53\u6D41 */\n getLocalStream(): MediaStream | null {\n return this.localStream;\n }\n\n /** \u83B7\u53D6\u8FDC\u7A0B\u5A92\u4F53\u6D41 */\n getRemoteStream(): MediaStream | null {\n return this.remoteStream;\n }\n\n /**\n * \u5207\u6362\u9759\u97F3\u72B6\u6001\n * @returns \u5207\u6362\u540E\u7684\u9759\u97F3\u72B6\u6001\uFF08true = \u5DF2\u9759\u97F3\uFF09\n */\n toggleMute(): boolean {\n if (!this.localStream) return this.isMuted;\n\n const audioTracks = this.localStream.getAudioTracks();\n for (const track of audioTracks) {\n track.enabled = this.isMuted;\n }\n this.isMuted = !this.isMuted;\n this.debugLogFn('CallSession', 'toggleMute', this.isMuted);\n return this.isMuted;\n }\n\n /**\n * \u5207\u6362\u89C6\u9891\u5F00\u5173\uFF08\u4EC5\u89C6\u9891\u901A\u8BDD\u6709\u6548\uFF09\n * @returns \u5207\u6362\u540E\u7684\u89C6\u9891\u72B6\u6001\uFF08true = \u89C6\u9891\u5F00\u542F\uFF09\n */\n toggleVideo(): boolean {\n if (!this.hasVideo || !this.localStream) return this.isVideoEnabled;\n\n const videoTracks = this.localStream.getVideoTracks();\n for (const track of videoTracks) {\n track.enabled = !this.isVideoEnabled;\n }\n this.isVideoEnabled = !this.isVideoEnabled;\n this.debugLogFn('CallSession', 'toggleVideo', this.isVideoEnabled);\n return this.isVideoEnabled;\n }\n\n /** \u6302\u65AD\u901A\u8BDD */\n hangUp(): void {\n this.debugLogFn('CallSession', 'hangUp', this.peerId);\n this.mediaConnection.close();\n }\n\n /**\n * \u5173\u95ED\u901A\u8BDD\u4F1A\u8BDD\n * \u505C\u6B62\u672C\u5730\u6D41\uFF0C\u6E05\u7406\u8D44\u6E90\n */\n close(): void {\n if (this.localStream) {\n this.localStream.getTracks().forEach(track => track.stop());\n this.localStream = null;\n }\n this.remoteStream = null;\n this._state = 'ended';\n }\n\n /** \u6CE8\u518C\u901A\u8BDD\u72B6\u6001\u53D8\u5316\u76D1\u542C\u5668 */\n onStateChange(listener: CallStateListener): void {\n this.stateListeners.add(listener);\n }\n\n /** \u79FB\u9664\u901A\u8BDD\u72B6\u6001\u53D8\u5316\u76D1\u542C\u5668 */\n offStateChange(listener: CallStateListener): void {\n this.stateListeners.delete(listener);\n }\n\n /**\n * \u901A\u77E5\u72B6\u6001\u53D8\u5316\n * @param state \u65B0\u72B6\u6001\n * @param reason \u53D8\u5316\u539F\u56E0\uFF08\u53EF\u9009\uFF09\n */\n private notifyStateChange(state: CallState, reason?: string): void {\n this.debugLogFn('CallSession', 'stateChange', { peer: this.peerId, state, reason });\n this.stateListeners.forEach(listener => {\n try {\n listener(state, reason);\n } catch (err) {\n this.debugLogFn('CallSession', 'listenerError', err);\n }\n });\n\n // \u901A\u8BDD\u7ED3\u675F\u65F6\u6267\u884C\u6E05\u7406\n if (state === 'ended') {\n this.onCleanup(this);\n }\n }\n}\n", "/**\n * \u5E38\u91CF\u5B9A\u4E49\n * \u96C6\u4E2D\u7BA1\u7406\u6240\u6709\u9B54\u6CD5\u6570\u5B57\u548C\u914D\u7F6E\u503C\n */\n\n/** \u8FDE\u63A5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09 */\nexport const CONNECTION_TIMEOUT_MS = 30000;\n\n/** \u53D1\u9001\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09 */\nexport const SEND_TIMEOUT_MS = 10000;\n\n/** \u91CD\u8FDE\u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09 */\nexport const RECONNECT_DELAY_MS = 1000;\n\n/** \u8DEF\u7531\u8FC7\u671F\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 */\nexport const ROUTE_EXPIRE_AGE_MS = 5 * 60 * 1000;\n\n/** \u8DEF\u7531\u8868\u5BB9\u91CF\u9650\u5236 */\nexport const MAX_ROUTING_ENTRIES = 50;\n\n/** \u76F4\u8FDE\u8282\u70B9\u5BB9\u91CF\u9650\u5236 */\nexport const MAX_DIRECT_NODES = 5;\n\n/** \u8DEF\u7531\u6E05\u7406\u5468\u671F\uFF08\u6BEB\u79D2\uFF09 */\nexport const ROUTE_CLEANUP_INTERVAL_MS = 60 * 1000;\n\n/** \u8DEF\u7531\u5E7F\u64AD\u5468\u671F\uFF08\u6BEB\u79D2\uFF09 */\nexport const ROUTE_BROADCAST_INTERVAL_MS = 30 * 1000;\n\n/** \u9ED8\u8BA4 TTL\uFF08Time To Live\uFF09- \u6D88\u606F\u6700\u5927\u8DF3\u6570 */\nexport const DEFAULT_TTL = 128;\n", "/**\n * RoutingDB.ts - \u8DEF\u7531\u8868 IndexedDB \u6301\u4E45\u5316\u6A21\u5757\n *\n * \u63D0\u4F9B\u8DEF\u7531\u8868\u7684\u6301\u4E45\u5316\u5B58\u50A8\u529F\u80FD\uFF0C\u4F7F\u7528 IndexedDB \u800C\u975E localStorage\n * \u4EE5\u652F\u6301\u5927\u89C4\u6A21\u8DEF\u7531\u8868\u5B58\u50A8\n *\n * \u6570\u636E\u5E93\u7ED3\u6784\uFF1A\n * - peerjs-routing-db (\u7248\u672C 1)\n * - routing-table: \u8DEF\u7531\u8868\u6761\u76EE (keyPath: target)\n * - direct-nodes: \u76F4\u8FDE\u8282\u70B9 (keyPath: nodeId)\n */\n\nimport type { RouteEntry, DirectNodeLatency } from './types';\nimport { ROUTE_EXPIRE_AGE_MS } from './constants';\n\nconst DB_NAME = 'peerjs-routing-db';\nconst DB_VERSION = 1;\nconst ROUTING_TABLE_STORE = 'routing-table';\nconst DIRECT_NODES_STORE = 'direct-nodes';\n\nlet db: IDBDatabase | null = null;\n\n/**\n * \u521B\u5EFA\u901A\u7528\u7684 Object Store \u64CD\u4F5C\n * @param storeName Object Store \u540D\u79F0\n * @param mode \u4E8B\u52A1\u6A21\u5F0F\n * @param operation \u8981\u6267\u884C\u7684\u64CD\u4F5C\n * @returns Promise<T>\n */\nfunction withStore<T>(\n storeName: string,\n mode: IDBTransactionMode,\n operation: (store: IDBObjectStore) => IDBRequest | void\n): Promise<T> {\n return openDB().then(database => {\n return new Promise<T>((resolve, reject) => {\n const tx = database.transaction(storeName, mode);\n const store = tx.objectStore(storeName);\n const request = operation(store) as IDBRequest;\n \n if (request) {\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n } else {\n tx.oncomplete = () => resolve(undefined as T);\n tx.onerror = () => reject(tx.error);\n }\n });\n });\n}\n\n/**\n * \u6253\u5F00\u6570\u636E\u5E93\u8FDE\u63A5\n * @returns Promise<IDBDatabase>\n */\nfunction openDB(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n if (db) {\n resolve(db);\n return;\n }\n\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n db = request.result;\n resolve(db);\n };\n\n request.onupgradeneeded = (e: IDBVersionChangeEvent) => {\n const req = e.target as IDBOpenDBRequest;\n const database = req.result;\n\n if (!database.objectStoreNames.contains(ROUTING_TABLE_STORE)) {\n const routingStore = database.createObjectStore(ROUTING_TABLE_STORE, {\n keyPath: 'target',\n });\n routingStore.createIndex('timestamp', 'timestamp', { unique: false });\n }\n\n if (!database.objectStoreNames.contains(DIRECT_NODES_STORE)) {\n const nodesStore = database.createObjectStore(DIRECT_NODES_STORE, {\n keyPath: 'nodeId',\n });\n nodesStore.createIndex('timestamp', 'timestamp', { unique: false });\n }\n };\n });\n}\n\n/**\n * \u521D\u59CB\u5316\u6570\u636E\u5E93\n * @returns Promise<void>\n */\nexport async function initRoutingDB(): Promise<void> {\n await openDB();\n}\n\n/**\n * \u4FDD\u5B58\u8DEF\u7531\u8868\u6761\u76EE\n * @param entry \u8DEF\u7531\u8868\u6761\u76EE\n */\nexport async function saveRouteEntry(entry: RouteEntry): Promise<void> {\n await withStore(ROUTING_TABLE_STORE, 'readwrite', store => store.put(entry));\n}\n\n/**\n * \u6279\u91CF\u4FDD\u5B58\u8DEF\u7531\u8868\u6761\u76EE\n * @param entries \u8DEF\u7531\u8868\u6761\u76EE\u6570\u7EC4\n */\nexport async function saveRouteEntries(entries: RouteEntry[]): Promise<void> {\n await openDB().then(database => {\n return new Promise<void>((resolve, reject) => {\n const tx = database.transaction(ROUTING_TABLE_STORE, 'readwrite');\n const store = tx.objectStore(ROUTING_TABLE_STORE);\n\n for (const entry of entries) {\n store.put(entry);\n }\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n });\n}\n\n/**\n * \u5220\u9664\u8DEF\u7531\u8868\u6761\u76EE\n * @param target \u76EE\u6807\u8282\u70B9 ID\n */\nexport async function deleteRouteEntry(target: string): Promise<void> {\n await withStore(ROUTING_TABLE_STORE, 'readwrite', store => store.delete(target));\n}\n\n/**\n * \u52A0\u8F7D\u5168\u90E8\u8DEF\u7531\u8868\n * @returns Promise<RouteEntry[]>\n */\nexport async function loadRoutingTable(): Promise<RouteEntry[]> {\n return withStore<RouteEntry[]>(ROUTING_TABLE_STORE, 'readonly', store => store.getAll());\n}\n\n/**\n * \u6E05\u7406\u8FC7\u671F\u8DEF\u7531\u8868\u6761\u76EE\n * @param maxAgeMs \u6700\u5927\u4FDD\u7559\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 5 \u5206\u949F\n * @returns Promise<number> \u5220\u9664\u7684\u6761\u76EE\u6570\u91CF\n */\nexport async function cleanupExpiredRoutes(maxAgeMs: number = ROUTE_EXPIRE_AGE_MS): Promise<number> {\n const database = await openDB();\n const now = Date.now();\n\n return new Promise((resolve, reject) => {\n const tx = database.transaction(ROUTING_TABLE_STORE, 'readwrite');\n const store = tx.objectStore(ROUTING_TABLE_STORE);\n const index = store.index('timestamp');\n const range = IDBKeyRange.upperBound(now - maxAgeMs);\n const request = index.openCursor(range);\n let deletedCount = 0;\n\n request.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest).result;\n if (cursor) {\n cursor.delete();\n deletedCount++;\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(deletedCount);\n tx.onerror = () => reject(tx.error);\n });\n}\n\n/**\n * \u4FDD\u5B58\u76F4\u8FDE\u8282\u70B9\n * @param node \u76F4\u8FDE\u8282\u70B9\n */\nexport async function saveDirectNode(node: DirectNodeLatency): Promise<void> {\n await withStore(DIRECT_NODES_STORE, 'readwrite', store => store.put(node));\n}\n\n/**\n * \u6279\u91CF\u4FDD\u5B58\u76F4\u8FDE\u8282\u70B9\n * @param nodes \u76F4\u8FDE\u8282\u70B9\u6570\u7EC4\n */\nexport async function saveDirectNodes(nodes: DirectNodeLatency[]): Promise<void> {\n await openDB().then(database => {\n return new Promise<void>((resolve, reject) => {\n const tx = database.transaction(DIRECT_NODES_STORE, 'readwrite');\n const store = tx.objectStore(DIRECT_NODES_STORE);\n\n for (const node of nodes) {\n store.put(node);\n }\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n });\n}\n\n/**\n * \u52A0\u8F7D\u5168\u90E8\u76F4\u8FDE\u8282\u70B9\n * @returns Promise<DirectNodeLatency[]>\n */\nexport async function loadDirectNodes(): Promise<DirectNodeLatency[]> {\n return withStore<DirectNodeLatency[]>(DIRECT_NODES_STORE, 'readonly', store => store.getAll());\n}\n\n/**\n * \u5220\u9664\u76F4\u8FDE\u8282\u70B9\n * @param nodeId \u8282\u70B9 ID\n */\nexport async function deleteDirectNode(nodeId: string): Promise<void> {\n await withStore(DIRECT_NODES_STORE, 'readwrite', store => store.delete(nodeId));\n}\n\n/**\n * \u6E05\u7406\u8FC7\u671F\u76F4\u8FDE\u8282\u70B9\n * @param maxAgeMs \u6700\u5927\u4FDD\u7559\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 5 \u5206\u949F\n * @returns Promise<number> \u5220\u9664\u7684\u8282\u70B9\u6570\u91CF\n */\nexport async function cleanupExpiredNodes(maxAgeMs: number = ROUTE_EXPIRE_AGE_MS): Promise<number> {\n const database = await openDB();\n const now = Date.now();\n\n return new Promise((resolve, reject) => {\n const tx = database.transaction(DIRECT_NODES_STORE, 'readwrite');\n const store = tx.objectStore(DIRECT_NODES_STORE);\n const index = store.index('timestamp');\n const range = IDBKeyRange.upperBound(now - maxAgeMs);\n const request = index.openCursor(range);\n let deletedCount = 0;\n\n request.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest).result;\n if (cursor) {\n cursor.delete();\n deletedCount++;\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(deletedCount);\n tx.onerror = () => reject(tx.error);\n });\n}\n\n/**\n * \u6E05\u9664\u5168\u90E8\u8DEF\u7531\u6570\u636E\n * @returns Promise<void>\n */\nexport async function clearAllRoutingData(): Promise<void> {\n const database = await openDB();\n\n await new Promise<void>((resolve, reject) => {\n const tx = database.transaction(ROUTING_TABLE_STORE, 'readwrite');\n const store = tx.objectStore(ROUTING_TABLE_STORE);\n store.clear();\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n\n await new Promise<void>((resolve, reject) => {\n const tx = database.transaction(DIRECT_NODES_STORE, 'readwrite');\n const store = tx.objectStore(DIRECT_NODES_STORE);\n store.clear();\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n", "/**\n * Router - \u8DEF\u7531\u7BA1\u7406\u5668\n * \n * \u8D1F\u8D23\u4E2D\u7EE7\u901A\u4FE1\u7684\u6838\u5FC3\u529F\u80FD\uFF1A\n * - \u7EF4\u62A4\u76F4\u8FDE\u8282\u70B9\u5217\u8868\u53CA\u5EF6\u8FDF\uFF08directNodes\uFF09\n * - \u7EF4\u62A4\u8DEF\u7531\u8868\uFF08routingTable\uFF09\uFF1A\u76EE\u6807\u8282\u70B9 -> \u591A\u4E2A\u4E0B\u4E00\u8DF3\uFF08\u542B\u5EF6\u8FDF\uFF09\n * - \u5E7F\u64AD\u8DEF\u7531\u66F4\u65B0\u5230\u90BB\u5C45\u8282\u70B9\n * - \u8DEF\u7531\u53D1\u73B0\uFF1A\u5F53\u76F4\u8FDE\u548C\u8DEF\u7531\u8868\u90FD\u5931\u8D25\u65F6\uFF0C\u5E7F\u64AD\u8BE2\u95EE\u8C01\u80FD\u8FDE\u901A\u76EE\u6807\n * - \u5904\u7406\u8DEF\u7531\u67E5\u8BE2\u548C\u54CD\u5E94\n * \n * \u8DEF\u7531\u673A\u5236\uFF1A\n * 1. \u6BCF\u6B21\u6210\u529F\u901A\u4FE1\u540E\uFF0C\u8BB0\u5F55\u5BF9\u65B9\u8282\u70B9\u4E3A\u76F4\u8FDE\u8282\u70B9\u5E76\u6D4B\u91CF\u5EF6\u8FDF\n * 2. \u6210\u529F\u540E\u5E7F\u64AD\u8DEF\u7531\u66F4\u65B0\uFF0C\u544A\u77E5\u5BF9\u65B9\u81EA\u5DF1\u53EF\u8FBE\u7684\u8282\u70B9\n * 3. \u6536\u5230\u8DEF\u7531\u66F4\u65B0\u540E\uFF0C\u5408\u5E76\u5230\u672C\u5730\u8DEF\u7531\u8868\n * 4. \u76F4\u8FDE\u5931\u8D25\u4E14\u8DEF\u7531\u8868\u4E3A\u7A7A\u65F6\uFF0C\u6267\u884C\u8DEF\u7531\u53D1\u73B0\u5E7F\u64AD\n */\n\nimport type { Peer } from 'peerjs';\nimport type { RouteEntry, NextHop, DirectNodeLatency, RelayConfig, RelayMessage } from './types';\nimport {\n initRoutingDB,\n loadRoutingTable,\n saveRouteEntry,\n saveRouteEntries,\n deleteRouteEntry,\n loadDirectNodes,\n saveDirectNode,\n saveDirectNodes,\n cleanupExpiredRoutes,\n cleanupExpiredNodes,\n} from './RoutingDB';\nimport {\n MAX_ROUTING_ENTRIES,\n MAX_DIRECT_NODES,\n ROUTE_EXPIRE_AGE_MS,\n ROUTE_CLEANUP_INTERVAL_MS,\n ROUTE_BROADCAST_INTERVAL_MS,\n CONNECTION_TIMEOUT_MS,\n DEFAULT_TTL,\n} from './constants';\n\n/**\n * \u8DEF\u7531\u7BA1\u7406\u5668\u56DE\u8C03\u63A5\u53E3\n */\nexport interface RoutingCallbacks {\n /** \u83B7\u53D6\u672C\u5730 Peer ID */\n getMyPeerId(): string;\n /** \u83B7\u53D6 PeerJS \u5B9E\u4F8B */\n getPeerInstance(): Peer | null;\n /** \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570 */\n debugLog: (obj: string, event: string, data?: unknown) => void;\n /** \u53D1\u9001\u4E2D\u7EE7\u6D88\u606F */\n sendRelayMessage(targetId: string, message: RelayMessage): Promise<void>;\n /** \u5904\u7406\u8DEF\u7531\u67E5\u8BE2\u54CD\u5E94 */\n onRouteDiscoveryResponse?: (targetId: string, latency: number) => void;\n}\n\n/**\n * \u8DEF\u7531\u5668\u7C7B\n * \u8D1F\u8D23\u7EF4\u62A4\u8DEF\u7531\u8868\u3001\u8282\u70B9\u53D1\u73B0\u548C\u81EA\u52A8\u8DEF\u7531\u9009\u62E9\n */\nexport class Router {\n /** \u8DEF\u7531\u8868\uFF1Atarget -> RouteEntry */\n private routingTable = new Map<string, RouteEntry>();\n /** \u76F4\u8FDE\u8282\u70B9\u53CA\u5EF6\u8FDF\u5217\u8868 */\n private directNodes: DirectNodeLatency[] = [];\n /** \u4E2D\u7EE7\u914D\u7F6E */\n private relayConfig: RelayConfig;\n /** \u56DE\u8C03\u51FD\u6570\u96C6\u5408 */\n private callbacks: RoutingCallbacks;\n /** \u7B49\u5F85\u8DEF\u7531\u53D1\u73B0\u54CD\u5E94\u7684 pending \u961F\u5217 */\n private pendingRouteQueries = new Map<string, { resolve: (entry: RouteEntry) => void; reject: (err: Error) => void; timer: ReturnType<typeof setTimeout> }>();\n /** \u5B9A\u65F6\u6E05\u7406\u5B9A\u65F6\u5668 */\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n /** \u5468\u671F\u5E7F\u64AD\u5B9A\u65F6\u5668 */\n private broadcastTimer: ReturnType<typeof setInterval> | null = null;\n\n /**\n * \u521B\u5EFA\u8DEF\u7531\u7BA1\u7406\u5668\n * @param callbacks \u56DE\u8C03\u51FD\u6570\u96C6\u5408\n * @param relayConfig \u4E2D\u7EE7\u914D\u7F6E\uFF08\u53EF\u9009\uFF09\n */\n constructor(callbacks: RoutingCallbacks, relayConfig?: RelayConfig) {\n this.callbacks = callbacks;\n this.relayConfig = relayConfig ?? {};\n }\n\n /**\n * \u521D\u59CB\u5316\u8DEF\u7531\u7BA1\u7406\u5668\uFF08\u4ECE IndexedDB \u52A0\u8F7D\u6570\u636E\u5E76\u542F\u52A8\u5B9A\u65F6\u4EFB\u52A1\uFF09\n */\n async init(): Promise<void> {\n await initRoutingDB();\n await this.loadFromDB();\n this.startMaintenanceTasks();\n }\n\n /**\n * \u4ECE IndexedDB \u52A0\u8F7D\u8DEF\u7531\u6570\u636E\n */\n private async loadFromDB(): Promise<void> {\n try {\n const routes = await loadRoutingTable();\n for (const entry of routes) {\n this.routingTable.set(entry.target, entry);\n }\n this.callbacks.debugLog('Routing', 'loadedRoutes', routes.length);\n } catch (e) {\n this.callbacks.debugLog('Routing', 'loadError', e);\n }\n\n try {\n const nodes = await loadDirectNodes();\n this.directNodes = nodes;\n this.callbacks.debugLog('Routing', 'loadedNodes', nodes.length);\n } catch (e) {\n this.callbacks.debugLog('Routing', 'loadNodesError', e);\n }\n }\n\n /**\n * \u542F\u52A8\u5B9A\u65F6\u7EF4\u62A4\u4EFB\u52A1\n */\n private startMaintenanceTasks(): void {\n this.cleanupTimer = setInterval(() => {\n this.cleanupExpiredEntries();\n }, ROUTE_CLEANUP_INTERVAL_MS);\n\n this.broadcastTimer = setInterval(() => {\n this.broadcastRouteUpdate();\n }, ROUTE_BROADCAST_INTERVAL_MS);\n }\n\n /**\n * \u6E05\u7406\u8FC7\u671F\u8DEF\u7531\u6761\u76EE\n */\n private async cleanupExpiredEntries(): Promise<void> {\n const now = Date.now();\n\n for (const [target, entry] of this.routingTable) {\n if (now - entry.timestamp > ROUTE_EXPIRE_AGE_MS) {\n this.routingTable.delete(target);\n deleteRouteEntry(target).catch(() => {});\n }\n }\n\n this.directNodes = this.directNodes.filter((node) => {\n const isExpired = now - node.timestamp > ROUTE_EXPIRE_AGE_MS;\n if (isExpired) {\n return false;\n }\n return true;\n });\n\n this.callbacks.debugLog('Routing', 'cleanup', {\n routes: this.routingTable.size,\n nodes: this.directNodes.length,\n });\n }\n\n /**\n * \u6301\u4E45\u5316\u8DEF\u7531\u8868\u5230 IndexedDB\n */\n async persist(): Promise<void> {\n try {\n const entries = Array.from(this.routingTable.values());\n await saveRouteEntries(entries);\n } catch (e) {\n this.callbacks.debugLog('Routing', 'persistError', e);\n }\n\n try {\n await saveDirectNodes(this.directNodes);\n } catch (e) {\n this.callbacks.debugLog('Routing', 'persistNodesError', e);\n }\n }\n\n /**\n * \u9500\u6BC1\u8DEF\u7531\u7BA1\u7406\u5668\uFF08\u6E05\u7406\u5B9A\u65F6\u5668\uFF09\n */\n destroy(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n if (this.broadcastTimer) {\n clearInterval(this.broadcastTimer);\n this.broadcastTimer = null;\n }\n this.pendingRouteQueries.forEach((pending) => {\n clearTimeout(pending.timer);\n });\n this.pendingRouteQueries.clear();\n }\n\n /**\n * \u8BB0\u5F55\u6210\u529F\u7684\u76F4\u8FDE\u901A\u4FE1\n * @param nodeId \u8282\u70B9 ID\n * @param latency \u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09\n */\n recordDirectNode(nodeId: string, latency: number): void {\n const myPeerId = this.callbacks.getMyPeerId();\n if (nodeId === myPeerId) return;\n\n const existing = this.directNodes.find(n => n.nodeId === nodeId);\n const timestamp = Date.now();\n\n if (existing) {\n existing.latency = latency;\n existing.timestamp = timestamp;\n } else {\n const maxRelayNodes = this.relayConfig.maxRelayNodes ?? MAX_DIRECT_NODES;\n this.directNodes.push({ nodeId, latency, timestamp });\n if (this.directNodes.length > maxRelayNodes) {\n this.directNodes.sort((a, b) => a.latency - b.latency);\n this.directNodes.shift();\n }\n }\n\n this.callbacks.debugLog('Routing', 'directNode', { nodeId, latency });\n }\n\n /**\n * \u83B7\u53D6\u76F4\u8FDE\u8282\u70B9\u5217\u8868\uFF08\u6309\u5EF6\u8FDF\u5347\u5E8F\uFF09\n * @returns \u76F4\u8FDE\u8282\u70B9\u5217\u8868\n */\n getDirectNodes(): DirectNodeLatency[] {\n return [...this.directNodes].sort((a, b) => a.latency - b.latency);\n }\n\n /**\n * \u68C0\u67E5\u662F\u5426\u53EF\u4EE5\u76F4\u8FDE\u76EE\u6807\u8282\u70B9\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @returns \u662F\u5426\u53EF\u4EE5\u76F4\u8FDE\n */\n canReachDirectly(targetId: string): boolean {\n return this.directNodes.some(n => n.nodeId === targetId);\n }\n\n /**\n * \u83B7\u53D6\u5230\u76F4\u8FDE\u8282\u70B9\u7684\u5EF6\u8FDF\n * @param nodeId \u8282\u70B9 ID\n * @returns \u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09\uFF0C\u5982\u679C\u4E0D\u5B58\u5728\u8FD4\u56DE null\n */\n getDirectLatency(nodeId: string): number | null {\n const node = this.directNodes.find(n => n.nodeId === nodeId);\n return node ? node.latency : null;\n }\n\n /**\n * \u79FB\u9664\u5931\u6548\u7684\u8DEF\u7531\uFF08\u901A\u4FE1\u5931\u8D25\u65F6\u8C03\u7528\uFF09\n * @param nodeId \u5931\u6548\u7684\u8282\u70B9 ID\n */\n removeRoute(nodeId: string): void {\n let removed = false;\n for (const [target, entry] of this.routingTable) {\n const originalLength = entry.nextHops.length;\n entry.nextHops = entry.nextHops.filter(h => h.nodeId !== nodeId);\n if (entry.nextHops.length === 0) {\n this.routingTable.delete(target);\n deleteRouteEntry(target).catch(() => {});\n removed = true;\n } else if (entry.nextHops.length < originalLength) {\n entry.timestamp = Date.now();\n saveRouteEntry(entry).catch(() => {});\n removed = true;\n }\n }\n\n const nodeIndex = this.directNodes.findIndex(n => n.nodeId === nodeId);\n if (nodeIndex !== -1) {\n this.directNodes.splice(nodeIndex, 1);\n removed = true;\n }\n\n if (removed) {\n this.callbacks.debugLog('Routing', 'routeRemoved', nodeId);\n }\n }\n\n /**\n * \u68C0\u67E5\u8DEF\u7531\u8868\u662F\u5426\u4E3A\u7A7A\n * @returns \u662F\u5426\u4E3A\u7A7A\n */\n isRoutingTableEmpty(): boolean {\n return this.routingTable.size === 0;\n }\n\n /**\n * \u8BB0\u5F55\u6210\u529F\u901A\u4FE1\u7684\u8282\u70B9\uFF08\u517C\u5BB9\u65E7\u63A5\u53E3\uFF09\n * @param nodeId \u8282\u70B9 ID\n */\n recordSuccessfulNode(nodeId: string): void {\n const myPeerId = this.callbacks.getMyPeerId();\n if (nodeId === myPeerId) return;\n\n const existing = this.directNodes.find(n => n.nodeId === nodeId);\n if (!existing) {\n const maxRelayNodes = this.relayConfig.maxRelayNodes ?? 5;\n this.directNodes.push({ nodeId, latency: 100, timestamp: Date.now() });\n if (this.directNodes.length > maxRelayNodes) {\n this.directNodes.sort((a, b) => a.latency - b.latency);\n this.directNodes.shift();\n }\n }\n }\n\n /**\n * \u5E7F\u64AD\u8DEF\u7531\u66F4\u65B0\n * \u5411\u6240\u6709\u76F4\u8FDE\u8282\u70B9\u53D1\u9001\u8DEF\u7531\u66F4\u65B0\u6D88\u606F\uFF0C\u544A\u77E5\u5B83\u4EEC\u672C\u8282\u70B9\u53EF\u8FBE\u7684\u8282\u70B9\u5217\u8868\n */\n async broadcastRouteUpdate(): Promise<void> {\n const myPeerId = this.callbacks.getMyPeerId();\n const reachableNodes = this.getReachableNodes();\n\n for (const node of this.directNodes) {\n try {\n await this.sendRouteUpdate(node.nodeId, reachableNodes);\n } catch {\n // \u5FFD\u7565\u5355\u4E2A\u8282\u70B9\u7684\u5E7F\u64AD\u5931\u8D25\n }\n }\n }\n\n /**\n * \u83B7\u53D6\u672C\u8282\u70B9\u53EF\u8FBE\u7684\u8282\u70B9\u5217\u8868\n * @returns \u53EF\u8FBE\u8282\u70B9\u6570\u7EC4\uFF08\u76F4\u8FDE\u8282\u70B9 + \u81EA\u5DF1\uFF09\n */\n private getReachableNodes(): string[] {\n const myPeerId = this.callbacks.getMyPeerId();\n const directNodeIds = this.directNodes.map(n => n.nodeId);\n return [...new Set([...directNodeIds, myPeerId])];\n }\n\n /**\n * \u53D1\u9001\u8DEF\u7531\u66F4\u65B0\u5230\u6307\u5B9A\u8282\u70B9\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param reachableNodes \u53EF\u8FBE\u7684\u8282\u70B9\u5217\u8868\n */\n private async sendRouteUpdate(targetId: string, reachableNodes: string[]): Promise<void> {\n const myPeerId = this.callbacks.getMyPeerId();\n\n const message: RelayMessage = {\n type: 'route-update',\n id: `${myPeerId}-route-${Date.now()}`,\n originalTarget: targetId,\n relayPath: [],\n forwardPath: [],\n ttl: DEFAULT_TTL,\n routeUpdate: { reachableNodes },\n };\n\n await this.callbacks.sendRelayMessage(targetId, message);\n }\n\n /**\n * \u5904\u7406\u6536\u5230\u7684\u8DEF\u7531\u66F4\u65B0\n * \u5408\u5E76\u5BF9\u7AEF\u53D1\u6765\u7684\u53EF\u8FBE\u8282\u70B9\u4FE1\u606F\u5230\u672C\u5730\u8DEF\u7531\u8868\n * @param fromPeerId \u53D1\u9001\u8DEF\u7531\u66F4\u65B0\u7684\u8282\u70B9 ID\n * @param message \u8DEF\u7531\u66F4\u65B0\u6D88\u606F\n */\n handleRouteUpdate(fromPeerId: string, message: RelayMessage): void {\n if (!message.routeUpdate) return;\n\n const myPeerId = this.callbacks.getMyPeerId();\n const { reachableNodes } = message.routeUpdate;\n const timestamp = Date.now();\n\n const viaLatency = this.getDirectLatency(fromPeerId) ?? 100;\n\n for (const target of reachableNodes) {\n if (target === myPeerId) continue;\n\n let entry = this.routingTable.get(target);\n const totalLatency = viaLatency + 100;\n\n if (!entry) {\n entry = {\n target,\n nextHops: [],\n hops: 1,\n timestamp,\n };\n this.routingTable.set(target, entry);\n }\n\n const existingHop = entry.nextHops.find(h => h.nodeId === fromPeerId);\n if (existingHop) {\n existingHop.latency = totalLatency;\n } else {\n entry.nextHops.push({ nodeId: fromPeerId, latency: totalLatency });\n }\n\n entry.nextHops.sort((a, b) => a.latency - b.latency);\n entry.hops = Math.min(entry.hops, 1);\n entry.timestamp = timestamp;\n\n this.callbacks.debugLog('Routing', 'update', { target, nextHop: fromPeerId, latency: totalLatency });\n }\n }\n\n /**\n * \u6267\u884C\u8DEF\u7531\u53D1\u73B0\u5E7F\u64AD\n * \u5F53\u76F4\u8FDE\u548C\u8DEF\u7531\u8868\u90FD\u5931\u8D25\u65F6\uFF0C\u5411\u6240\u6709\u76F4\u8FDE\u8282\u70B9\u5E7F\u64AD\u8BE2\u95EE\u8C01\u80FD\u8FDE\u901A\u76EE\u6807\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @returns \u8DEF\u7531\u6761\u76EE\uFF08\u5982\u679C\u53D1\u73B0\uFF09\n */\n async discoverRoute(targetId: string): Promise<RouteEntry | null> {\n const myPeerId = this.callbacks.getMyPeerId();\n const directNodes = this.getDirectNodes();\n\n if (directNodes.length === 0) {\n this.callbacks.debugLog('Routing', 'discoverRoute', 'no direct nodes');\n return null;\n }\n\n this.callbacks.debugLog('Routing', 'discoverRoute', { targetId, directNodes: directNodes.length });\n\n const queryId = `${myPeerId}-query-${Date.now()}`;\n\n const routeEntry = await new Promise<RouteEntry | null>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRouteQueries.delete(queryId);\n resolve(null);\n }, CONNECTION_TIMEOUT_MS);\n\n this.pendingRouteQueries.set(queryId, { resolve, reject, timer });\n\n const message: RelayMessage = {\n type: 'route-query',\n id: queryId,\n originalTarget: targetId,\n relayPath: [myPeerId],\n forwardPath: [],\n ttl: DEFAULT_TTL,\n routeQuery: {\n queryOrigin: myPeerId,\n targetNode: targetId,\n queryPath: [myPeerId],\n },\n };\n\n for (const node of directNodes) {\n this.callbacks.sendRelayMessage(node.nodeId, message).catch(() => {});\n }\n });\n\n return routeEntry;\n }\n\n /**\n * \u5904\u7406\u8DEF\u7531\u67E5\u8BE2\u6D88\u606F\n * @param fromPeerId \u53D1\u9001\u67E5\u8BE2\u7684\u8282\u70B9\n * @param message \u8DEF\u7531\u67E5\u8BE2\u6D88\u606F\n */\n handleRouteQuery(fromPeerId: string, message: RelayMessage): void {\n if (!message.routeQuery) return;\n\n const myPeerId = this.callbacks.getMyPeerId();\n const { queryOrigin, targetNode, queryPath } = message.routeQuery;\n\n if (targetNode === myPeerId) {\n const latency = this.getDirectLatency(fromPeerId) ?? 100;\n const response: RelayMessage = {\n type: 'route-response',\n id: `${myPeerId}-resp-${Date.now()}`,\n originalTarget: queryOrigin,\n relayPath: [],\n forwardPath: [],\n ttl: DEFAULT_TTL,\n routeResponse: {\n queryOrigin,\n responder: myPeerId,\n targetNode,\n latency,\n },\n };\n this.callbacks.sendRelayMessage(fromPeerId, response);\n return;\n }\n\n if (queryPath.includes(myPeerId)) {\n return;\n }\n\n const currentTTL = message.ttl ?? DEFAULT_TTL;\n if (currentTTL <= 0) {\n this.callbacks.debugLog('Routing', 'ttlExpired', { type: 'route-query', id: message.id });\n return;\n }\n\n const nextHop = this.findNextHopToTarget(targetNode);\n if (nextHop) {\n const latency = (this.getDirectLatency(fromPeerId) ?? 100) + nextHop.latency;\n const response: RelayMessage = {\n type: 'route-response',\n id: `${myPeerId}-resp-${Date.now()}`,\n originalTarget: queryOrigin,\n relayPath: [],\n forwardPath: [],\n ttl: DEFAULT_TTL,\n routeResponse: {\n queryOrigin,\n responder: myPeerId,\n targetNode,\n latency,\n },\n };\n this.callbacks.sendRelayMessage(fromPeerId, response);\n return;\n }\n\n const newPath = [...queryPath, myPeerId];\n const forwardMessage: RelayMessage = {\n ...message,\n relayPath: newPath,\n ttl: currentTTL - 1,\n routeQuery: {\n ...message.routeQuery,\n queryPath: newPath,\n },\n };\n\n for (const node of this.directNodes) {\n if (node.nodeId !== fromPeerId) {\n this.callbacks.sendRelayMessage(node.nodeId, forwardMessage).catch(() => {});\n }\n }\n }\n\n /**\n * \u5904\u7406\u8DEF\u7531\u67E5\u8BE2\u54CD\u5E94\n * @param fromPeerId \u54CD\u5E94\u8005\u8282\u70B9\n * @param message \u8DEF\u7531\u54CD\u5E94\u6D88\u606F\n */\n handleRouteResponse(fromPeerId: string, message: RelayMessage): void {\n if (!message.routeResponse) return;\n\n const { queryOrigin, targetNode, latency } = message.routeResponse;\n const myPeerId = this.callbacks.getMyPeerId();\n\n if (queryOrigin !== myPeerId) return;\n\n const pending = Array.from(this.pendingRouteQueries.values())[0];\n if (!pending) return;\n\n let entry = this.routingTable.get(targetNode);\n const timestamp = Date.now();\n\n if (!entry) {\n entry = {\n target: targetNode,\n nextHops: [],\n hops: 1,\n timestamp,\n };\n this.routingTable.set(targetNode, entry);\n }\n\n const existingHop = entry.nextHops.find(h => h.nodeId === fromPeerId);\n if (existingHop) {\n existingHop.latency = latency;\n } else {\n entry.nextHops.push({ nodeId: fromPeerId, latency });\n }\n\n entry.nextHops.sort((a, b) => a.latency - b.latency);\n entry.timestamp = timestamp;\n\n this.callbacks.debugLog('Routing', 'discovered', { targetNode, nextHop: fromPeerId, latency });\n\n clearTimeout(pending.timer);\n pending.resolve(entry);\n }\n\n /**\n * \u67E5\u627E\u5230\u76EE\u6807\u8282\u70B9\u7684\u4E0B\u4E00\u8DF3\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @returns \u4E0B\u4E00\u8DF3\u4FE1\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u8FD4\u56DE null\n */\n findNextHopToTarget(targetId: string): NextHop | null {\n const entry = this.routingTable.get(targetId);\n if (!entry || entry.nextHops.length === 0) return null;\n return entry.nextHops[0];\n }\n\n /**\n * \u83B7\u53D6\u5230\u76EE\u6807\u8282\u70B9\u7684\u6240\u6709\u4E0B\u4E00\u8DF3\uFF08\u6309\u5EF6\u8FDF\u5347\u5E8F\uFF09\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @returns \u4E0B\u4E00\u8DF3\u5217\u8868\n */\n getNextHopsToTarget(targetId: string): NextHop[] {\n const entry = this.routingTable.get(targetId);\n return entry ? [...entry.nextHops] : [];\n }\n\n /**\n * \u83B7\u53D6\u8DEF\u7531\u8868\n * @returns \u8DEF\u7531\u8868\u5BF9\u8C61\n */\n getRoutingTable(): Record<string, RouteEntry> {\n const result: Record<string, RouteEntry> = {};\n this.routingTable.forEach((entry, target) => {\n result[target] = { ...entry, nextHops: [...entry.nextHops] };\n });\n return result;\n }\n\n /**\n * \u83B7\u53D6\u5DF2\u77E5\u8282\u70B9\u5217\u8868\uFF08\u517C\u5BB9\u65E7\u63A5\u53E3\uFF09\n * @returns \u8282\u70B9 ID \u6570\u7EC4\n */\n getKnownNodes(): string[] {\n return this.directNodes.map(n => n.nodeId);\n }\n}\n", "/**\n * MessageHandler - \u6D88\u606F\u5904\u7406\u6A21\u5757\n * \n * \u8D1F\u8D23\u5904\u7406\u63A5\u6536\u5230\u7684\u5404\u7C7B\u6D88\u606F\uFF1A\n * - \u76F4\u8FDE\u8BF7\u6C42\uFF08request\uFF09\n * - \u4E2D\u7EE7\u8BF7\u6C42\uFF08relay-request\uFF09\n * - \u8DEF\u7531\u66F4\u65B0\uFF08route-update\uFF09\n * \n * \u4E2D\u7EE7\u8BF7\u6C42\u5904\u7406\u6D41\u7A0B\uFF1A\n * 1. \u6536\u5230 relay-request \u6D88\u606F\n * 2. \u5982\u679C\u662F\u76EE\u6807\u8282\u70B9\uFF0C\u5904\u7406\u8BF7\u6C42\u5E76\u8FD4\u56DE\u54CD\u5E94\n * 3. \u5982\u679C\u4E0D\u662F\u76EE\u6807\u8282\u70B9\uFF0C\u6839\u636E forwardPath \u8F6C\u53D1\u5230\u4E0B\u4E00\u4E2A\u8282\u70B9\n * 4. \u5982\u679C forwardPath \u4E3A\u7A7A\uFF0C\u5C1D\u8BD5\u76F4\u8FDE\u5230\u76EE\u6807\u8282\u70B9\n * \n * @example\n * const handler = new MessageHandler(callbacks);\n * const response = await handler.handleRequest(from, request, relayMessage);\n */\n\nimport type { Peer } from 'peerjs';\nimport type { Request, Response, SimpleHandler, RelayMessage } from './types';\nimport { CONNECTION_TIMEOUT_MS, DEFAULT_TTL } from './constants';\n\n/**\n * \u6D88\u606F\u5904\u7406\u5668\u56DE\u8C03\u63A5\u53E3\n */\nexport interface MessageHandlerCallbacks {\n /** \u83B7\u53D6\u672C\u5730 Peer ID */\n getMyPeerId(): string;\n /** \u83B7\u53D6 PeerJS \u5B9E\u4F8B */\n getPeerInstance(): Peer | null;\n /** \u7B49\u5F85\u8FDE\u63A5\u5C31\u7EEA */\n waitForReady(): Promise<void>;\n /** \u83B7\u53D6\u5904\u7406\u5668\u6620\u5C04\u8868 */\n getSimpleHandlers(): Map<string, SimpleHandler>;\n /** \u8C03\u8BD5\u65E5\u5FD7\u51FD\u6570 */\n debugLog: (obj: string, event: string, data?: unknown) => void;\n /** \u8DEF\u7531\u66F4\u65B0\u56DE\u8C03\uFF08\u53EF\u9009\uFF09 */\n onRouteUpdate?: (fromPeerId: string, message: RelayMessage) => void;\n}\n\n/**\n * \u6D88\u606F\u5904\u7406\u5668\u7C7B\n * \u8D1F\u8D23\u89E3\u6790\u548C\u5904\u7406\u63A5\u6536\u5230\u7684\u5404\u7C7B\u6D88\u606F\n */\nexport class MessageHandler {\n /** \u56DE\u8C03\u51FD\u6570\u96C6\u5408 */\n private callbacks: MessageHandlerCallbacks;\n\n /**\n * \u521B\u5EFA\u6D88\u606F\u5904\u7406\u5668\n * @param callbacks \u56DE\u8C03\u51FD\u6570\u96C6\u5408\n */\n constructor(callbacks: MessageHandlerCallbacks) {\n this.callbacks = callbacks;\n }\n\n /**\n * \u5904\u7406\u6536\u5230\u7684\u8BF7\u6C42\n * \u6839\u636E\u662F\u5426\u6709\u4E2D\u7EE7\u6D88\u606F\u4E0A\u4E0B\u6587\u5224\u65AD\u662F\u76F4\u8FDE\u8BF7\u6C42\u8FD8\u662F\u4E2D\u7EE7\u8BF7\u6C42\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @param relayMessage \u4E2D\u7EE7\u6D88\u606F\u4E0A\u4E0B\u6587\uFF08\u53EF\u9009\uFF0C\u6709\u5219\u4E3A\u4E2D\u7EE7\u8BF7\u6C42\uFF09\n * @returns \u54CD\u5E94\u6570\u636E\n */\n async handleRequest(from: string, request: Request, relayMessage?: RelayMessage): Promise<Response> {\n if (relayMessage) {\n return this.handleRelayRequest(from, request, relayMessage);\n }\n return this.handleDirectRequest(from, request);\n }\n\n /**\n * \u5904\u7406\u76F4\u8FDE\u8BF7\u6C42\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async handleDirectRequest(from: string, request: Request): Promise<Response> {\n const result = await this.processHandler(request.path, from, request.data);\n if (this.isErrorResponse(result)) {\n return result as Response;\n }\n return { status: 200, data: result };\n }\n\n /**\n * \u5904\u7406\u4E2D\u7EE7\u8BF7\u6C42\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @param relayMessage \u4E2D\u7EE7\u6D88\u606F\u4E0A\u4E0B\u6587\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async handleRelayRequest(from: string, request: Request, relayMessage: RelayMessage): Promise<Response> {\n const myPeerId = this.callbacks.getMyPeerId();\n const { originalTarget, relayPath, forwardPath } = relayMessage;\n\n if (myPeerId === originalTarget) {\n const result = await this.processHandler(request.path, from, request.data);\n if (this.isErrorResponse(result)) {\n return result as Response;\n }\n return { status: 200, data: result };\n }\n\n const currentTTL = relayMessage.ttl ?? DEFAULT_TTL;\n if (currentTTL <= 0) {\n this.callbacks.debugLog('MessageHandler', 'ttlExpired', { type: 'relay-request', id: relayMessage.id });\n return {\n status: 502,\n data: { error: 'TTL expired - message dropped to prevent routing loop' },\n };\n }\n\n if (forwardPath.length > 0) {\n const nextHop = forwardPath[0];\n const remainingPath = forwardPath.slice(1);\n \n try {\n const response = await this.forwardRelay(nextHop, {\n type: 'relay-request',\n id: relayMessage.id,\n originalTarget,\n relayPath: [...relayPath, myPeerId],\n forwardPath: remainingPath,\n ttl: currentTTL - 1,\n request,\n });\n return response;\n } catch (err) {\n return {\n status: 500,\n data: { error: `Forward failed: ${err instanceof Error ? err.message : 'Unknown error'}` },\n };\n }\n }\n\n try {\n const data = await this.forwardToTarget(originalTarget, request, relayMessage);\n return { status: 200, data };\n } catch (err) {\n return {\n status: 500,\n data: { error: `Forward to target failed: ${err instanceof Error ? err.message : 'Unknown error'}` },\n };\n }\n }\n\n /**\n * \u8C03\u7528\u6CE8\u518C\u7684\u5904\u7406\u5668\n * @param path \u8BF7\u6C42\u8DEF\u5F84\n * @param from \u53D1\u9001\u8005 Peer ID\n * @param data \u8BF7\u6C42\u6570\u636E\n * @returns \u5904\u7406\u5668\u8FD4\u56DE\u7684\u6570\u636E\uFF0C\u6216 404 \u9519\u8BEF\n */\n async processHandler(path: string, from: string, data?: unknown): Promise<unknown> {\n const handlers = this.callbacks.getSimpleHandlers();\n const simpleHandler = handlers.get(path);\n if (simpleHandler) {\n return await simpleHandler(from, data);\n }\n // \u8FD4\u56DE 404 \u9519\u8BEF\u54CD\u5E94\u683C\u5F0F\n return { status: 404, data: { error: `Path not found: ${path}` } };\n }\n\n /**\n * \u5224\u65AD\u7ED3\u679C\u662F\u5426\u4E3A\u9519\u8BEF\u54CD\u5E94\n */\n private isErrorResponse(result: unknown): result is { status: number; data: unknown } {\n return typeof result === 'object' && result !== null && 'status' in result && 'data' in result;\n }\n\n /**\n * \u521B\u5EFA\u8FDE\u63A5\u5E76\u53D1\u9001\u4E2D\u7EE7\u6D88\u606F\u7684\u901A\u7528\u65B9\u6CD5\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param message \u8981\u53D1\u9001\u7684\u6D88\u606F\n * @param extractResponse \u4ECE\u54CD\u5E94\u6D88\u606F\u4E2D\u63D0\u53D6\u6570\u636E\u7684\u51FD\u6570\n * @returns Promise<Response>\n */\n private createConnectionAndSend(\n targetId: string,\n message: RelayMessage,\n extractResponse: (response: RelayMessage) => Response | null\n ): Promise<Response> {\n return new Promise((resolve, reject) => {\n this.callbacks.debugLog('MessageHandler', 'createConnectionAndSend', { targetId });\n\n this.callbacks.waitForReady()\n .then(() => {\n const peerInstance = this.callbacks.getPeerInstance();\n if (!peerInstance) {\n reject(new Error('Peer instance not available'));\n return;\n }\n\n const conn = peerInstance.connect(targetId, { reliable: true });\n\n const timeout = setTimeout(() => {\n conn.close();\n reject(new Error(`Connection timeout: ${targetId}`));\n }, CONNECTION_TIMEOUT_MS);\n\n conn.on('open', () => {\n this.callbacks.debugLog('Conn', 'open', targetId);\n conn.send(message);\n });\n\n conn.on('data', (responseData: unknown) => {\n const response = responseData as RelayMessage;\n const extracted = extractResponse(response);\n if (extracted) {\n clearTimeout(timeout);\n conn.close();\n resolve(extracted);\n }\n });\n\n conn.on('error', (err) => {\n this.callbacks.debugLog('Conn', 'error', { peer: targetId, error: err });\n clearTimeout(timeout);\n reject(err);\n });\n\n conn.on('close', () => {\n this.callbacks.debugLog('Conn', 'close', targetId);\n clearTimeout(timeout);\n reject(new Error('Connection closed'));\n });\n })\n .catch(reject);\n });\n }\n\n /**\n * \u8F6C\u53D1\u4E2D\u7EE7\u8BF7\u6C42\u5230\u4E0B\u4E00\u4E2A\u8282\u70B9\n * @param nextHop \u4E0B\u4E00\u8DF3\u8282\u70B9 ID\n * @param message \u8981\u8F6C\u53D1\u7684\u6D88\u606F\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async forwardRelay(nextHop: string, message: RelayMessage): Promise<Response> {\n return this.createConnectionAndSend(\n nextHop,\n message,\n (response) => {\n if (response.type === 'relay-response' && response.response) {\n return response.response;\n }\n return null;\n }\n );\n }\n\n /**\n * \u8F6C\u53D1\u5230\u6700\u7EC8\u76EE\u6807\u8282\u70B9\uFF08\u5F53\u6CA1\u6709\u66F4\u591A\u4E2D\u7EE7\u8282\u70B9\u65F6\u4F7F\u7528\uFF09\n * @param targetId \u76EE\u6807\u8282\u70B9 ID\n * @param request \u8BF7\u6C42\u6570\u636E\n * @param originalMessage \u539F\u59CB\u4E2D\u7EE7\u6D88\u606F\n * @returns \u54CD\u5E94\u6570\u636E\n */\n private async forwardToTarget(targetId: string, request: Request, originalMessage: RelayMessage): Promise<unknown> {\n const myPeerId = this.callbacks.getMyPeerId();\n const currentTTL = originalMessage.ttl ?? DEFAULT_TTL;\n \n const message: RelayMessage = {\n type: 'relay-request',\n id: originalMessage.id,\n originalTarget: originalMessage.originalTarget,\n relayPath: [...originalMessage.relayPath, myPeerId],\n forwardPath: [],\n ttl: currentTTL - 1,\n request,\n };\n\n const response = await this.createConnectionAndSend(\n targetId,\n message,\n (resp) => {\n if (resp.type === 'relay-response' && resp.response) {\n return resp.response;\n }\n return null;\n }\n );\n\n return response.data;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }