import { BridgeMessage, PendingRequest, MethodHandler, BridgeOptions, EventCallback } from "../types"; import { generateId, validateMessage, Logger, BridgeError } from "../utils"; import { EventBus } from "./EventBus"; export class FastBridge { // 存储等待响应的请求 private pendingRequests: Map = new Map(); // 存储注册的方法 private registeredMethods: Map = new Map(); // 事件总线 private eventBus: EventBus; // 日志工具 private logger: Logger; // 命名空间 private namespace: string; // 角色:host 或 client private role: "host" | "client"; // iframe 元素(仅宿主页面有) private iframe?: HTMLIFrameElement; // 安全配置 private strictOrigin: boolean; private allowedOrigins: Set; // 连接状态 private isConnected: boolean = false; // 是否正在连接 private isConnecting: boolean = false; // 连接 Promise private connectionPromise: Promise | null = null; // 等待发送的消息队列 private pendingMessages: BridgeMessage[] = []; // MessageChannel 端口 private port: MessagePort | null = null; // 消息统计 private messageStats = { sent: 0, received: 0, errors: 0, lastSent: 0, lastReceived: 0, }; constructor(options: BridgeOptions) { this.namespace = options.namespace || "default"; this.iframe = options.iframe; this.role = this.iframe ? "host" : "client"; // 安全配置 this.strictOrigin = options.security?.strictOrigin !== false; // 默认严格 this.allowedOrigins = new Set(options.security?.allowedOrigins || []); this.logger = new Logger({ debug: options.debug || false, role: this.role.toUpperCase() as "HOST" | "CLIENT", }); this.eventBus = new EventBus(); // 注册内置方法 this._registerBuiltinMethods(); this.logger.log(`FastBridge 初始化完成,角色: ${this.role}, 严格源验证: ${this.strictOrigin}`); } // 初始化方法 - 用户只需要调用这个方法,内部处理所有时序问题 async init(): Promise { // 如果已经连接,直接返回 if (this.isConnected) { this.logger.log("已经连接,跳过连接过程"); return; } // 如果正在连接,等待连接完成 if (this.isConnecting && this.connectionPromise) { this.logger.log("正在连接中,等待连接完成"); return this.connectionPromise; } // 开始连接 this.isConnecting = true; this.connectionPromise = this._connect(); try { await this.connectionPromise; } finally { this.isConnecting = false; } } // 实际的连接逻辑 - 内部处理所有时序问题 private async _connect(): Promise { this.logger.log("开始建立连接..."); if (this.role === "host") { // 宿主页面:等待 iframe 准备就绪,然后建立连接 await this._connectAsHost(); } else { // iframe 页面:等待宿主连接 await this._connectAsClient(); } this.isConnected = true; this.logger.log("连接建立成功"); // 发送队列中的消息 this._flushPendingMessages(); // 触发连接状态变化事件 this.eventBus.emit("connectionChange", true); } // 宿主连接逻辑 - 自动处理 iframe 加载和准备就绪 private async _connectAsHost(): Promise { this.logger.log("以宿主身份连接..."); if (!this.iframe) { throw new Error("宿主页面必须提供 iframe 元素"); } // 标准连接模式:等待 iframe 完全准备就绪 // 1. 等待 iframe 加载完成(优化后更快) await this._waitForIframeLoad(); // 2. 等待 iframe 准备就绪(减少超时时间) await this._waitForIframeReady(); // 3. 创建 MessageChannel 并发送给 iframe await this._establishMessageChannel(); } // 等待 iframe 加载完成 private async _waitForIframeLoad(): Promise { if (!this.iframe) { throw new Error("宿主页面必须提供 iframe 元素"); } return new Promise((resolve) => { // 检查 iframe 是否已经加载完成 if (this.iframe!.contentWindow && this.iframe!.contentDocument?.readyState === "complete") { // iframe 已经加载完成 this.logger.log("iframe 已加载完成"); resolve(); return; } // 检查 iframe 是否已经加载到 interactive 状态 if (this.iframe!.contentDocument?.readyState === "interactive") { this.logger.log("iframe 已加载到交互状态"); resolve(); return; } // 等待 iframe 加载完成 this.iframe!.onload = () => { this.logger.log("iframe onload 事件触发"); resolve(); }; // 设置超时,避免无限等待 setTimeout(() => { this.logger.warn("iframe 加载超时,尝试继续连接"); resolve(); }, 1000); // 固定 1 秒超时 }); } // 等待 iframe 准备就绪 private async _waitForIframeReady(): Promise { return new Promise((resolve) => { const handleMessage = (event: MessageEvent) => { // 验证消息来源 if (!this._isOriginAllowed(event.origin)) { this.logger.warn(`拒绝来自未授权源的消息: ${event.origin}`); return; } if (event.data?.type === "iframe-ready" && event.data?.namespace === this.namespace) { window.removeEventListener("message", handleMessage); this.logger.log("iframe 已准备就绪"); resolve(); } }; window.addEventListener("message", handleMessage); // 如果 iframe 已经加载完成,减少等待时间 const timeout = this.iframe?.contentDocument?.readyState === "complete" ? 500 : 2000; setTimeout(() => { window.removeEventListener("message", handleMessage); this.logger.warn(`等待 iframe 准备就绪超时(${timeout}ms),尝试直接连接`); resolve(); }, timeout); }); } // 验证源是否被允许 private _isOriginAllowed(origin: string): boolean { // 如果不严格验证源,检查允许列表 if (!this.strictOrigin) { if (this.allowedOrigins.size === 0) { return true; // 允许所有源 } return this.allowedOrigins.has(origin); } // 严格验证:只允许同源和 iframe 源 // 如果是同源,允许 if (origin === window.location.origin) { return true; } // 如果是 iframe 的源,允许 if (this.iframe && this.iframe.src) { try { const iframeUrl = new URL(this.iframe.src); if (origin === iframeUrl.origin) { return true; } } catch (error) { // URL 解析失败,可能是相对路径 return true; // 相对路径认为是安全的 } } // 其他源拒绝 return false; } // 建立 MessageChannel 连接 private async _establishMessageChannel(): Promise { if (!this.iframe) { throw new Error("宿主页面必须提供 iframe 元素"); } // 创建 MessageChannel const channel = new MessageChannel(); const port1 = channel.port1; // 宿主使用 port1 const port2 = channel.port2; // 发送给 iframe // 设置消息监听 port1.onmessage = (event) => { this._handleMessage(event.data); }; // 发送初始化消息给 iframe,同时传递 port2 const initMessage = { type: "init", namespace: this.namespace, channelId: generateId(), timestamp: Date.now(), }; // 获取 iframe 的 origin const targetOrigin = this._getIframeOrigin(); this.logger.log("发送初始化消息给 iframe"); this.iframe.contentWindow!.postMessage(initMessage, targetOrigin, [port2]); // 保存端口和目标窗口 this.port = port1; // this.targetWindow = this.iframe.contentWindow; // 删除未使用的targetWindow } // 获取 iframe 的 origin private _getIframeOrigin(): string { if (!this.iframe || !this.iframe.src) { return window.location.origin; // 默认使用当前源 } try { const iframeUrl = new URL(this.iframe.src); return iframeUrl.origin; } catch (error) { // URL 解析失败,可能是相对路径,使用当前源 return window.location.origin; } } // iframe 客户端连接逻辑 private async _connectAsClient(): Promise { this.logger.log("以客户端身份等待连接..."); // 1. 通知宿主已准备就绪 this._notifyHostReady(); // 2. 等待宿主发送 MessageChannel await this._waitForHostConnection(); } // 通知宿主已准备就绪 private _notifyHostReady(): void { const readyMessage = { type: "iframe-ready", namespace: this.namespace, timestamp: Date.now(), }; this.logger.log("通知宿主已准备就绪"); // 使用父窗口的 origin,而不是 "*" window.parent.postMessage(readyMessage, window.parent.location.origin); } // 等待宿主连接 private async _waitForHostConnection(): Promise { return new Promise((resolve) => { const handleMessage = (event: MessageEvent) => { // 验证消息来源 if (!this._isOriginAllowed(event.origin)) { this.logger.warn(`拒绝来自未授权源的消息: ${event.origin}`); return; } if (event.data?.type === "init" && event.data?.namespace === this.namespace) { // 获取传递过来的端口 const port = event.ports[0]; if (port) { // 设置消息监听 port.onmessage = (portEvent) => { this._handleMessage(portEvent.data); }; // 保存端口和目标窗口 this.port = port; // this.targetWindow = window.parent; // 删除未使用的targetWindow this.logger.log("收到宿主的 MessageChannel 连接"); window.removeEventListener("message", handleMessage); resolve(); } } }; window.addEventListener("message", handleMessage); }); } // 发送队列中的消息 private _flushPendingMessages(): void { if (this.pendingMessages.length > 0) { this.logger.log(`发送队列中的 ${this.pendingMessages.length} 条消息`); this.pendingMessages.forEach((message) => { this._sendMessage(message); }); this.pendingMessages = []; } } // 发送消息 private _sendMessage(message: BridgeMessage): void { if (!this.port) { this.logger.warn("连接未建立,消息已加入队列", message); this.pendingMessages.push(message); return; } // 更新发送统计 this.messageStats.sent++; this.messageStats.lastSent = Date.now(); // 序列化消息以确保可以安全传输 const serializedMessage = this._serializeMessage(message); try { this.port.postMessage(serializedMessage); this.logger.log("发送消息:", message); } catch (error) { this.logger.error("发送消息失败:", error); this.messageStats.errors++; } } // 序列化消息,处理不可克隆的对象 private _serializeMessage(message: BridgeMessage): BridgeMessage { const serialized = { ...message }; // 处理请求消息的参数 if (serialized.type === "request" && serialized.params) { serialized.params = this._serializeData(serialized.params); } // 处理响应消息的结果 if (serialized.type === "response" && serialized.result) { serialized.result = this._serializeData(serialized.result); } // 处理事件消息的数据 if (serialized.type === "event" && serialized.data) { serialized.data = this._serializeData(serialized.data); } return serialized; } // 序列化数据,移除不可克隆的属性 private _serializeData(data: any): any { if (data === null || data === undefined) { return data; } if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") { return data; } if (Array.isArray(data)) { return data.map((item) => this._serializeData(item)); } if (typeof data === "object") { // 处理Date对象 if (data instanceof Date) { return { __type: "Date", value: data.toISOString() }; } // 处理RegExp对象 if (data instanceof RegExp) { return { __type: "RegExp", source: data.source, flags: data.flags }; } // 处理Error对象 if (data instanceof Error) { return { __type: "Error", name: data.name, message: data.message, stack: data.stack, }; } // 处理普通对象,移除不可克隆的属性 const serialized: any = {}; for (const [key, value] of Object.entries(data)) { // 跳过函数、Symbol、DOM节点等不可克隆的属性 if ( typeof value === "function" || typeof value === "symbol" || value instanceof Node || value instanceof Window || value instanceof Element ) { continue; } try { serialized[key] = this._serializeData(value); } catch (error) { this.logger.warn(`跳过不可序列化的属性: ${key}`, error); } } return serialized; } return data; } // 处理接收到的消息 private _handleMessage(message: any): void { // 更新接收统计 this.messageStats.received++; this.messageStats.lastReceived = Date.now(); // 反序列化消息 const deserializedMessage = this._deserializeMessage(message); // 验证消息格式 if (!validateMessage(deserializedMessage)) { this.logger.warn("收到无效消息格式:", deserializedMessage); this.messageStats.errors++; return; } // 验证命名空间 if (deserializedMessage.namespace !== this.namespace) { this.logger.warn("收到不匹配命名空间的消息:", deserializedMessage.namespace); this.messageStats.errors++; return; } this.logger.log("收到消息:", deserializedMessage); // 根据消息类型处理 switch (deserializedMessage.type) { case "request": this._handleRequest(deserializedMessage); break; case "response": this._handleResponse(deserializedMessage); break; case "event": this._handleEvent(deserializedMessage); break; default: this.logger.warn("未知消息类型:", deserializedMessage.type); this.messageStats.errors++; } } // 反序列化消息,恢复特殊对象 private _deserializeMessage(message: any): any { const deserialized = { ...message }; // 处理请求消息的参数 if (deserialized.type === "request" && deserialized.params) { deserialized.params = this._deserializeData(deserialized.params); } // 处理响应消息的结果 if (deserialized.type === "response" && deserialized.result) { deserialized.result = this._deserializeData(deserialized.result); } // 处理事件消息的数据 if (deserialized.type === "event" && deserialized.data) { deserialized.data = this._deserializeData(deserialized.data); } return deserialized; } // 反序列化数据,恢复特殊对象 private _deserializeData(data: any): any { if (data === null || data === undefined) { return data; } if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") { return data; } if (Array.isArray(data)) { return data.map((item) => this._deserializeData(item)); } if (typeof data === "object") { // 恢复Date对象 if (data.__type === "Date") { return new Date(data.value); } // 恢复RegExp对象 if (data.__type === "RegExp") { return new RegExp(data.source, data.flags); } // 恢复Error对象 if (data.__type === "Error") { const error = new Error(data.message); error.name = data.name; error.stack = data.stack; return error; } // 处理普通对象 const deserialized: any = {}; for (const [key, value] of Object.entries(data)) { deserialized[key] = this._deserializeData(value); } return deserialized; } return data; } // 处理请求消息 private async _handleRequest(message: any): Promise { const { id, method, params } = message; if (!method) { this._sendErrorResponse(id, "方法名是必需的"); return; } const handler = this.registeredMethods.get(method); if (!handler) { this._sendErrorResponse(id, `方法 '${method}' 未找到`); return; } try { this.logger.log(`处理请求: ${method}`); const result = await handler(params); this._sendResponse(id, result); } catch (error) { this._sendErrorResponse(id, error instanceof Error ? error.message : "未知错误"); } } // 处理响应消息 private _handleResponse(message: any): void { const { id, result, error } = message; const pendingRequest = this.pendingRequests.get(id); if (!pendingRequest) { this.logger.warn("未找到ID对应的待处理请求:", id); return; } this.pendingRequests.delete(id); if (error) { this.logger.log(`请求失败: ${error}`); pendingRequest.reject(new BridgeError(error, "REMOTE_ERROR")); } else { this.logger.log("请求成功"); pendingRequest.resolve(result); } } // 处理事件消息 private _handleEvent(message: any): void { const { event, data } = message; if (event) { this.logger.log(`处理事件: ${event}`); this.eventBus.emit(event, data); } } // 发送响应 private _sendResponse(id: string, result: any): void { const message = { id, type: "response" as const, namespace: this.namespace, result, timestamp: Date.now(), }; this._sendMessage(message); } // 发送错误响应 private _sendErrorResponse(id: string, error: string): void { const message = { id, type: "response" as const, namespace: this.namespace, error, timestamp: Date.now(), }; this._sendMessage(message); } // 注册方法供对方调用 register(methodName: string, handler: MethodHandler): void { this.registeredMethods.set(methodName, handler); this.logger.log(`方法已注册: ${methodName}`); } // 调用对方注册的方法 async call(methodName: string, params?: any): Promise { this.logger.log(`调用方法: ${methodName}`); const id = generateId(); const message = { id, type: "request" as const, namespace: this.namespace, method: methodName, params, timestamp: Date.now(), }; return new Promise((resolve, reject) => { // 存储待处理请求 this.pendingRequests.set(id, { resolve, reject }); // 发送消息 this._sendMessage(message); }); } // 发送事件 emit(eventName: string, data?: any): void { this.logger.log(`发送事件: ${eventName}`); const message = { id: generateId(), type: "event" as const, namespace: this.namespace, event: eventName, data, timestamp: Date.now(), }; this._sendMessage(message); } // 监听事件 on(eventName: string, callback: EventCallback): void { this.eventBus.on(eventName, callback); this.logger.log(`事件监听器已添加: ${eventName}`); } // 监听连接状态变化 onConnectionChange(callback: (connected: boolean) => void): void { this.eventBus.on("connectionChange", callback); } // 获取事件总线 getEventBus(): EventBus { return this.eventBus; } // 获取连接状态 getConnectionStatus(): boolean { return this.isConnected; } // 获取连接信息 getConnectionInfo(): { connected: boolean; connecting: boolean; role: "host" | "client"; namespace: string; targetOrigin?: string; } { return { connected: this.isConnected, connecting: this.isConnecting, role: this.role, namespace: this.namespace, targetOrigin: this.iframe ? this._getIframeOrigin() : undefined, }; } // 断开连接 disconnect(): void { this.logger.log("断开连接"); // 清理连接状态 this.isConnected = false; this.isConnecting = false; this.connectionPromise = null; // 关闭端口 if (this.port) { this.port.close(); this.port = null; } // 清理目标窗口 // this.targetWindow = null; // 删除未使用的targetWindow // 清理待处理请求 this.pendingRequests.clear(); // 清理消息队列 this.pendingMessages.length = 0; // 触发连接状态变化事件 this.eventBus.emit("connectionChange", false); this.logger.log("连接已断开"); } // 重连机制 async reconnect(): Promise { this.logger.log("尝试重连..."); // 先断开当前连接 this.disconnect(); // 等待一小段时间 await new Promise((resolve) => setTimeout(resolve, 100)); // 重新连接 await this.init(); } // 检查连接健康状态 async ping(): Promise { if (!this.isConnected) { throw new BridgeError("连接未建立", "NOT_CONNECTED"); } const startTime = Date.now(); try { await this.call("__ping__", { timestamp: startTime }); return Date.now() - startTime; } catch (error) { throw new BridgeError("连接异常", "CONNECTION_ERROR"); } } // 注册内置方法 private _registerBuiltinMethods(): void { // 注册 ping 方法 this.register("__ping__", async (params) => { return { pong: true, timestamp: params.timestamp }; }); } // 获取消息统计 getMessageStats(): typeof this.messageStats { return { ...this.messageStats }; } // 重置消息统计 resetMessageStats(): void { this.messageStats = { sent: 0, received: 0, errors: 0, lastSent: 0, lastReceived: 0, }; } }