UNPKG

iframe-bridge-sdk

Version:

Universal iframe communication SDK - unified package for host and guest applications

199 lines 6.07 kB
import { BaseMessenger, SDKState } from './shared'; /** * Host SDK - 在宿主应用中运行,创建iframe并与Guest SDK通信 */ export class HostSDK extends BaseMessenger { // ==================== 构造函数 ==================== constructor(config) { super(); /** 是否已初始化 */ this.initialized = false; this.config = { width: '100%', height: '100%', timeout: 10000, src: './', selector: '#iframe-container', allowedOrigins: [], ...config, }; // 设置请求超时时间 this.setTimeout(this.config.timeout || 10000); } // ==================== 公共方法 ==================== /** * 初始化Host SDK * @param container 容器元素 * @returns Promise<void> */ async init() { if (this.initialized) { console.warn('HostSDK: Already initialized'); return; } this.initialized = true; this.setState(SDKState.INITIALIZING); try { // 创建iframe this.createIframe(); // 设置消息监听 this.setupMessageListener(); // 等待Guest SDK就绪 await this.waitForReady(); this.setState(SDKState.READY); console.info('HostSDK: Initialized successfully'); } catch (error) { this.setState(SDKState.IDLE); throw error; } } /** * 销毁Host SDK */ destroy() { if (this.getState() === SDKState.DESTROYED) { return; } // 清理消息监听器 if (this.messageListener) { window.removeEventListener('message', this.messageListener); this.messageListener = undefined; } // 移除iframe if (this.iframe) { this.iframe.remove(); this.iframe = undefined; } // 调用父类的销毁方法 super.destroy(); this.initialized = false; console.info('HostSDK: Destroyed'); } /** * 获取iframe元素 */ getIframe() { return this.iframe; } /** * 获取配置 */ getConfig() { return { ...this.config }; } /** * 更新配置 * @param newConfig 新配置 */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; // 如果iframe存在,更新样式 if (this.iframe) { if (newConfig.width) { this.iframe.style.width = newConfig.width; } if (newConfig.height) { this.iframe.style.height = newConfig.height; } } } /** * 重新加载iframe */ reload() { if (this.iframe) { const currentSrc = this.iframe.src; this.iframe.src = ''; this.iframe.src = currentSrc; } } // ==================== 受保护的方法 ==================== /** * 实现抽象方法:发送消息到iframe * @param message 要发送的消息 */ sendMessage(message) { if (!this.iframe?.contentWindow) { console.warn('Cannot send message: iframe not ready'); return; } if (this.getState() === SDKState.DESTROYED) { console.warn('Cannot send message: SDK is destroyed'); return; } try { this.iframe.contentWindow.postMessage(message, '*'); } catch (error) { console.error('Failed to send message to iframe:', error); } } // ==================== 私有方法 ==================== /** * 创建iframe元素 */ createIframe() { this.iframe = document.createElement('iframe'); // 设置基本属性 this.iframe.src = this.config.src; this.iframe.style.width = this.config.width || '100%'; this.iframe.style.height = this.config.height || '100%'; this.iframe.style.border = 'none'; this.iframe.style.display = 'block'; // 设置安全属性 this.iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups'); // 获取容器元素并添加iframe const parent = document.querySelector(this.config.selector); if (!parent) { throw new Error(`Container not found with selector: ${this.config.selector}`); } parent.appendChild(this.iframe); console.info('HostSDK: Iframe created with src:', this.config.src); } /** * 设置消息监听器 */ setupMessageListener() { this.messageListener = (event) => { // 验证消息来源 if (!this.isValidMessageSource(event)) { return; } // 处理接收到的消息 this.handleIncomingMessage(event.data); }; window.addEventListener('message', this.messageListener); } /** * 验证消息来源是否有效 * @param event 消息事件 */ isValidMessageSource(event) { // 检查是否来自我们的iframe if (event.source !== this.iframe?.contentWindow) { return false; } // 检查来源域名(如果配置了限制) if (this.config.allowedOrigins && this.config.allowedOrigins.length > 0) { return this.config.allowedOrigins.includes(event.origin); } return true; } /** * 等待Guest SDK就绪 * @returns Promise<void> */ waitForReady() { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Guest SDK ready timeout')); }, this.config.timeout); this.on('ready', () => { clearTimeout(timeout); resolve(); }); }); } } //# sourceMappingURL=host.js.map