From 41898189395290ac5728b9a63fb629b7e58b3e79 Mon Sep 17 00:00:00 2001 From: IndieKKY Date: Sat, 5 Oct 2024 20:17:22 +0800 Subject: [PATCH] fix --- src/messaging/ExtensionMessage.ts | 6 +- src/messaging/InjectMessage.ts | 6 +- src/messaging/Layer1Protocol.ts | 107 ++++++++++++++++++++++++++++ src/messaging/PortMessageHandler.ts | 102 -------------------------- src/messaging/useMessage.ts | 4 +- src/messaging/useMessageService.ts | 6 +- 6 files changed, 118 insertions(+), 113 deletions(-) create mode 100644 src/messaging/Layer1Protocol.ts delete mode 100644 src/messaging/PortMessageHandler.ts diff --git a/src/messaging/ExtensionMessage.ts b/src/messaging/ExtensionMessage.ts index 7e72528..640c552 100644 --- a/src/messaging/ExtensionMessage.ts +++ b/src/messaging/ExtensionMessage.ts @@ -1,5 +1,5 @@ import { MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ROUTE_MSG } from '@/consts/const' -import PortMessageHandler from './PortMessageHandler' +import Layer1Protocol from './Layer1Protocol' export type PortContext = { id: string @@ -7,7 +7,7 @@ export type PortContext = { tabId: number type: 'inject' | 'app' port: chrome.runtime.Port - portMessageHandler: PortMessageHandler + portMessageHandler: Layer1Protocol } class ExtensionMessage { @@ -86,7 +86,7 @@ class ExtensionMessage { if (tabId != null) { // @ts-ignore const portContext: PortContext = {id, name, tabId, port, type} - const portMessageHandler = new PortMessageHandler(async (value: MessageData) => { + const portMessageHandler = new Layer1Protocol(async (value: MessageData) => { return handler(value, portContext) }, port) portContext.portMessageHandler = portMessageHandler diff --git a/src/messaging/InjectMessage.ts b/src/messaging/InjectMessage.ts index fbad9c0..b85b0f5 100644 --- a/src/messaging/InjectMessage.ts +++ b/src/messaging/InjectMessage.ts @@ -1,9 +1,9 @@ import { MESSAGE_TARGET_APP, MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ROUTE_MSG } from '@/consts/const' -import PortMessageHandler from './PortMessageHandler' +import Layer1Protocol from './Layer1Protocol' class InjectMessage { port?: chrome.runtime.Port - portMessageHandler?: PortMessageHandler + portMessageHandler?: Layer1Protocol //类实例 methods?: { [key: string]: (params: any, context: MethodContext) => Promise @@ -71,7 +71,7 @@ class InjectMessage { this.port = chrome.runtime.connect(import.meta.env.VITE_EXTENSION_ID, { name: MESSAGE_TARGET_INJECT, }) - this.portMessageHandler = new PortMessageHandler(this.messageHandler, this.port) + this.portMessageHandler = new Layer1Protocol(this.messageHandler, this.port) this.portMessageHandler!.startListen() this.portMessageHandler!.init('inject') this.methods = methods diff --git a/src/messaging/Layer1Protocol.ts b/src/messaging/Layer1Protocol.ts new file mode 100644 index 0000000..6d3c2d3 --- /dev/null +++ b/src/messaging/Layer1Protocol.ts @@ -0,0 +1,107 @@ +// 请求信息 +type ReqMsg = { + id: string + // 类型 + type: 'req' | 'res' + // 请求 + req?: L1Req + // 响应 + res?: RespMsg +} + +// 响应信息 +type RespMsg = { + code: number + data?: T + msg?: string +} + +// 创建一个 Layer1Protocol 类,用于持久监听 port 并通过消息 ID 处理响应,支持超时 +class Layer1Protocol { + private port: chrome.runtime.Port + private timeout: number + private messageMap: Map void, timer: number }> + private handler: (value: L1Req) => Promise + private type?: 'inject' | 'app' + private tabId?: number + + constructor(handler: (value: L1Req) => Promise, port: chrome.runtime.Port, timeout = 30000) { // 默认超时 30 秒 + this.port = port; + this.timeout = timeout; + this.messageMap = new Map(); + this.handler = handler + } + + init(type: 'inject' | 'app', tabId?: number) { + this.type = type + this.tabId = tabId + this.port.postMessage({ + type, + tabId, + }) + } + + startListen() { + // 持久监听 port.onMessage + this.port.onMessage.addListener((msg: ReqMsg) => { + const { id, type, req, res } = msg; + if (type === 'req') { + this.handler(req!).then(res => { + const response: RespMsg = { + code: 200, + msg: 'success', + data: res + } + this.port.postMessage({ id, type: 'res', res: response }); + }).catch(error => { + const response: RespMsg = { + code: 500, + msg: error.message, + } + this.port.postMessage({ id, type: 'res', res: response }); + }); + } else if (type === 'res') { + if (this.messageMap.has(id)) { + const { resolve, timer } = this.messageMap.get(id)!; + // 清除超时定时器 + clearTimeout(timer); + // 移除消息 ID + this.messageMap.delete(id); + // 通过 ID 找到对应的 Promise 并 resolve + resolve(res!.data!); + }else { + console.error('unknown response message id: ', id) + } + } else { + console.error('unknown message type: ', type) + } + }); + } + + // 使用 Promise 发送消息并等待响应,支持超时 + sendMessage(req: L1Req): Promise { + const id = this._generateUniqueId(); + + return new Promise((resolve, reject) => { + // 设置一个超时定时器 + const timer = setTimeout(() => { + // 超时后执行 reject 并从 Map 中删除 + this.messageMap.delete(id); + reject(new Error(`Request timed out after ${this.timeout / 1000} seconds`)); + }, this.timeout); + + // 将 resolve 和 timer 函数与消息 ID 绑定,存入 Map + this.messageMap.set(id, { resolve, timer }); + + // 发送消息,并附带 ID + this.port.postMessage({ id, type: 'req', req }); + }); + } + + // 生成唯一 ID(简单示例,可以使用更复杂的生成策略) + _generateUniqueId() { + return crypto.randomUUID() + } +} + +export default Layer1Protocol \ No newline at end of file diff --git a/src/messaging/PortMessageHandler.ts b/src/messaging/PortMessageHandler.ts deleted file mode 100644 index 6c6f47d..0000000 --- a/src/messaging/PortMessageHandler.ts +++ /dev/null @@ -1,102 +0,0 @@ -export type RespMsg = { - code: number - data?: T - msg?: string -} - -export type PortMessageType = 'req' | 'res' - -export type PortMessage = { - msgId: string - msgType: PortMessageType - req?: Req - res?: RespMsg -} - -// 创建一个 PortMessageHandler 类,用于持久监听 port 并通过消息 ID 处理响应,支持超时 -class PortMessageHandler { - private port: chrome.runtime.Port - private timeout: number - private messageMap: Map void, timer: number }> - private handler: (value: Req) => Promise - private type?: 'inject' | 'app' - private tabId?: number - - constructor(handler: (value: Req) => Promise, port: chrome.runtime.Port, timeout = 30000) { // 默认超时 30 秒 - this.port = port; - this.timeout = timeout; - this.messageMap = new Map(); - this.handler = handler - } - - init(type: 'inject' | 'app', tabId?: number) { - this.type = type - this.tabId = tabId - this.port.postMessage({ - type, - tabId, - }) - } - - startListen() { - // 持久监听 port.onMessage - this.port.onMessage.addListener((msg: PortMessage) => { - console.log('msg', this.type, this.tabId, msg) - const { msgId, msgType, req, res } = msg; - if (msgType === 'req') { - this.handler(req!).then(res => { - const response: RespMsg = { - code: 200, - msg: 'success', - data: res - } - this.port.postMessage({ msgId, msgType: 'res', res: response }); - }).catch(error => { - const response: RespMsg = { - code: 500, - msg: error.message, - } - this.port.postMessage({ msgId, msgType: 'res', res: response }); - }); - } else if (msgType === 'res') { - if (this.messageMap.has(msgId)) { - const { resolve, timer } = this.messageMap.get(msgId)!; - // 清除超时定时器 - clearTimeout(timer); - // 处理完毕后,移除该消息 ID - this.messageMap.delete(msgId); - // 通过 ID 找到对应的 Promise 并 resolve - resolve(res!.data!); - } - } - }); - } - - // 使用 Promise 发送消息并等待响应,支持超时 - sendMessage(req: Req): Promise { - const msgId = this._generateUniqueId(); - - return new Promise((resolve, reject) => { - // 设置一个超时定时器 - const timer = setTimeout(() => { - // 超时后执行 reject 并从 Map 中删除 - this.messageMap.delete(msgId); - reject(new Error(`Request timed out after ${this.timeout / 1000} seconds`)); - }, this.timeout); - - // 将 resolve 和 timer 函数与消息 ID 绑定,存入 Map - this.messageMap.set(msgId, { resolve, timer }); - - // 发送消息,并附带 ID - this.port.postMessage({ msgId, msgType: 'req', req }); - console.log('sendMessage>>>', msgId, 'req', req) - }); - } - - // 生成唯一 ID(简单示例,可以使用更复杂的生成策略) - _generateUniqueId() { - return crypto.randomUUID() - } -} - -export default PortMessageHandler \ No newline at end of file diff --git a/src/messaging/useMessage.ts b/src/messaging/useMessage.ts index 034b91b..97d0271 100644 --- a/src/messaging/useMessage.ts +++ b/src/messaging/useMessage.ts @@ -1,12 +1,12 @@ import { MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ROUTE_MSG } from '@/consts/const' import { injectWaiter } from './useMessageService' import { useCallback } from 'react' -import PortMessageHandler from './PortMessageHandler' +import Layer1Protocol from './Layer1Protocol' const useMessage = () => { const sendExtension = useCallback(async (method: string, params?: any) => { // wait - const portMessageHandler = await injectWaiter.wait() as PortMessageHandler + const portMessageHandler = await injectWaiter.wait() as Layer1Protocol // send message const messageResult = await portMessageHandler.sendMessage({ from: 'app', diff --git a/src/messaging/useMessageService.ts b/src/messaging/useMessageService.ts index 41484e9..75d94a1 100644 --- a/src/messaging/useMessageService.ts +++ b/src/messaging/useMessageService.ts @@ -3,14 +3,14 @@ import { MESSAGE_TARGET_APP, } from '@/consts/const' import { Waiter } from '@kky002/kky-util' -import PortMessageHandler from './PortMessageHandler' +import Layer1Protocol from './Layer1Protocol' const debug = (...args: any[]) => { console.debug('[App Messaging]', ...args) } let portMessageHandlerInit: boolean = false -let portMessageHandler: PortMessageHandler | undefined +let portMessageHandler: Layer1Protocol | undefined // let postInjectMessage: (method: string, params: PostMessagePayload) => Promise | undefined export const injectWaiter = new Waiter(() => ({ @@ -75,7 +75,7 @@ const useMessageService = (methods?: { }, []) portMessageHandler = useMemo(() => { if (messageHandler && port) { - const pmh = new PortMessageHandler(messageHandler, port) + const pmh = new Layer1Protocol(messageHandler, port) //get tabId from url params let tabId = window.location.search.split('tabId=')[1]