@ticatec/iframe-message-bridge
Version:
A lightweight TypeScript library for reliable communication between parent window and multiple iframes using postMessage, supporting one-way messages, request-response patterns, broadcast messaging, timeout handling, and automatic resource cleanup.
116 lines (115 loc) • 3.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageBridgeClient = void 0;
class MessageBridgeClient {
constructor(targetOrigin) {
this.pending = new Map();
this.broadcastHandlers = new Map();
this.requestId = 0;
this.targetOrigin = targetOrigin;
this.boundHandleMessage = this.handleMessage.bind(this);
window.addEventListener('message', this.boundHandleMessage);
}
handleMessage(event) {
if (event.origin !== this.targetOrigin)
return;
const { data } = event;
if (!data || data.__bridge__ !== true)
return;
// 处理响应消息
if (data.type === 'response') {
const message = data;
const { requestId, result, error } = message;
const pending = this.pending.get(requestId);
if (pending) {
if (error)
pending.reject(new Error(error));
else
pending.resolve(result);
this.pending.delete(requestId);
}
}
// 处理广播消息
if (data.type === 'broadcast') {
const message = data;
const { eventName, data: broadcastData } = message;
const handler = this.broadcastHandlers.get(eventName);
if (handler) {
handler(broadcastData);
}
}
}
// 发送请求并等待响应
emit(eventName, data, timeout = 30000) {
const requestId = `req_${Date.now()}_${++this.requestId}`;
const message = {
__bridge__: true,
type: 'request',
requestId,
eventName,
data,
};
window.parent.postMessage(message, this.targetOrigin);
return new Promise((resolve, reject) => {
// 设置超时处理
const timeoutId = setTimeout(() => {
if (this.pending.has(requestId)) {
this.pending.delete(requestId);
reject(new Error(`Request timeout after ${timeout}ms for event: ${eventName}`));
}
}, timeout);
this.pending.set(requestId, {
timeoutId,
resolve: (result) => {
clearTimeout(timeoutId);
resolve(result);
},
reject: (error) => {
clearTimeout(timeoutId);
reject(error);
}
});
});
}
// 发送单向消息(不等待响应)
send(eventName, data) {
// 使用 null 作为 requestId 表示不需要响应
const message = {
__bridge__: true,
type: 'request',
requestId: null,
eventName,
data,
};
window.parent.postMessage(message, this.targetOrigin);
}
// 注册广播消息处理器
onBroadcast(eventName, handler) {
this.broadcastHandlers.set(eventName, handler);
}
// 取消注册广播消息处理器
offBroadcast(eventName) {
this.broadcastHandlers.delete(eventName);
}
// 清空所有广播消息处理器
clearBroadcastHandlers() {
this.broadcastHandlers.clear();
}
// 清空所有待处理的请求(拒绝所有等待中的Promise)
clearPendingRequests() {
this.pending.forEach(({ reject, timeoutId }) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
reject(new Error('Request cancelled - client destroyed'));
});
this.pending.clear();
}
// 销毁实例,移除全局监听器并清理所有资源
destroy() {
window.removeEventListener('message', this.boundHandleMessage);
this.clearPendingRequests();
this.clearBroadcastHandlers();
}
}
exports.MessageBridgeClient = MessageBridgeClient;