npm package: @iss-ai/window-message-bus
Description: window消息封装为bus
Latest version: 0.0.8

# @iss-ai/window-message-bus

Window 消息封装为 bus，提供基于 `postMessage` 的跨窗口通信解决方案。支持主窗口与 iframe 之间的双向消息传递，以及编辑器场景下的专用通信协议。

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## 特性

- 🚀 **跨窗口通信**：支持主窗口与 iframe 之间的双向消息传递
- 🔒 **安全验证**：支持来源域名白名单验证
- 📦 **多格式构建**：同时提供 CJS、ESM、UMD、IIFE 四种模块格式
- 🎯 **类型安全**：完整的 TypeScript 类型定义
- 🛠️ **编辑器专用**：内置 EditorMessageBus 用于编辑器与 iframe 通信
- 🧪 **测试友好**：基于 Jest 和 jsdom 的完整测试覆盖

## 安装

```bash
npm install @iss-ai/window-message-bus
```

或

```bash
pnpm add @iss-ai/window-message-bus
```

## 快速开始

### 基础用法 - WindowMessageBus

```typescript
import { WindowMessageBus } from '@iss-ai/window-message-bus';

// 创建实例
const bus = new WindowMessageBus({
  sourceId: 'main-window',
  debug: true,
});

// 监听消息
bus.on('custom-event', (data, context) => {
  console.log('收到消息:', data, context);
});

// 发送消息到父窗口
bus.emitTo('parent', 'custom-event', { message: 'Hello from iframe!' });

// 发送消息到指定窗口
bus.emitTo(window.parent, 'custom-event', { message: 'Hello parent!' });
```

### 编辑器场景 - EditorMessageBus

```typescript
import { EditorMessageBus } from '@iss-ai/window-message-bus';

// 主窗口（编辑器容器）
const editorBus = new EditorMessageBus({
  sourceId: 'editor-main',
  debug: true,
});

// 监听编辑器就绪
editorBus.onReady((data) => {
  console.log('编辑器已就绪:', data);
});

// 设置编辑器数据
editorBus.setData({ nodes: [], edges: [] });

// 监听内容变更
editorBus.onChange((change) => {
  console.log('内容变更:', change);
});

// 监听保存
editorBus.onSave((data) => {
  console.log('保存数据:', data);
});

// 发送保存指令
editorBus.save();

// iframe 端
const iframeEditorBus = new EditorMessageBus({
  sourceId: 'editor-iframe',
  debug: true,
});

// 通知主窗口已就绪
iframeEditorBus.isReady({ version: '1.0.0' });

// 接收设置数据指令
iframeEditorBus.onSetData((data) => {
  renderEditor(data);
});

// 内容变更时通知主窗口
iframeEditorBus.change({ type: 'update', elementId: 'node-1' });

// 导出数据
const data = await iframeEditorBus.exportData();
```

## API 文档

### WindowMessageBus

#### 构造函数选项

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `debug` | `boolean` | `false` | 是否开启调试模式 |
| `debugPrefix` | `string` | `'[WindowMessageBus]'` | 调试日志前缀 |
| `sourceId` | `string` | 自动生成 | 当前实例的唯一标识 |
| `allowedOrigins` | `string[]` | `['*']` | 允许的消息源域名 |
| `serialize` | `(data: any) => string` | `JSON.stringify` | 消息序列化函数 |
| `deserialize` | `(str: string) => any` | `JSON.parse` | 消息反序列化函数 |

#### 主要方法

| 方法 | 说明 |
|------|------|
| `emitTo(target, type, data)` | 向指定目标发送消息 |
| `on(type, handler)` | 监听消息事件 |
| `off(type, handler)` | 取消监听消息事件 |
| `once(type, handler)` | 监听一次消息事件 |
| `getSourceId()` | 获取当前实例的 sourceId |

#### PostMessageTarget 类型

```typescript
type PostMessageTarget = WindowProxy | 'parent' | 'self' | 'top';
```

### EditorMessageBus

继承自 `WindowMessageBus`，提供以下专用方法：

#### 就绪相关

| 方法 | 说明 |
|------|------|
| `isReady(data, target?)` | 发送编辑器已就绪消息 |
| `onReady(handler)` | 监听编辑器已就绪事件 |

#### 数据设置

| 方法 | 说明 |
|------|------|
| `setData(data, target?)` | 设置编辑器数据 |
| `onSetData(handler)` | 监听设置数据事件 |

#### 配置设置

| 方法 | 说明 |
|------|------|
| `setConfig(config, target?)` | 设置编辑器配置 |
| `onSetConfig(handler)` | 监听设置配置事件 |

#### 内容变更

| 方法 | 说明 |
|------|------|
| `change(changeData, target?)` | 发送内容变更消息 |
| `onChange(handler)` | 监听内容变更事件 |

#### 保存

| 方法 | 说明 |
|------|------|
| `save(options?, target?)` | 发送保存消息 |
| `onSave(handler)` | 监听保存事件 |

#### 导出功能

| 方法 | 说明 |
|------|------|
| `exportSVG(options?, target?)` | 导出 SVG |
| `onExportSVG(handler)` | 监听 SVG 导出响应 |
| `exportPNG(options?, target?)` | 导出 PNG |
| `onExportPNG(handler)` | 监听 PNG 导出响应 |
| `exportData(options?, target?)` | 导出数据 |
| `onExportData(handler)` | 监听数据导出响应 |

#### 其他功能

| 方法 | 说明 |
|------|------|
| `getData(target?)` | 获取编辑器数据 |
| `onGetData(handler)` | 监听获取数据响应 |
| `thumbnail(options?, target?)` | 生成缩略图 |
| `onThumnail(handler)` | 监听缩略图响应 |
| `preview(options?, target?)` | 预览 |
| `onPreview(handler)` | 监听预览响应 |

## 类型定义

### MessageData

```typescript
interface MessageData {
  type: string;      // 事件类型
  data: any;         // 消息数据
  messageId?: string; // 消息 ID
  sourceId?: string;  // 来源标识
}
```

### MessageContext

```typescript
interface MessageContext {
  origin: string;        // 消息来源域名
  source: WindowProxy;   // 消息来源窗口引用
  messageId?: string;    // 消息 ID
  sourceId?: string;     // 消息来源标识
}
```

### EditorData

```typescript
interface EditorData {
  [key: string]: any;
}
```

### EditorConfig

```typescript
interface EditorConfig {
  canvas?: {
    width?: number;
    height?: number;
    backgroundColor?: string;
    grid?: {
      enabled?: boolean;
      size?: number;
      color?: string;
    };
    snapToGrid?: boolean;
  };
  toolbar?: {
    enabled?: boolean;
    position?: 'top' | 'bottom' | 'left' | 'right';
    items?: string[];
  };
  propertiesPanel?: {
    enabled?: boolean;
    position?: 'left' | 'right';
    width?: number;
  };
  minimap?: {
    enabled?: boolean;
    position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
  };
  interaction?: {
    zoom?: { enabled?: boolean; minScale?: number; maxScale?: number };
    pan?: { enabled?: boolean; sensitivity?: number };
    selection?: { enabled?: boolean; multiple?: boolean };
    drag?: { enabled?: boolean; snap?: boolean };
  };
  theme?: {
    mode?: 'light' | 'dark' | 'auto';
    colors?: {
      primary?: string;
      secondary?: string;
      background?: string;
      surface?: string;
    };
  };
  [key: string]: any;
}
```

## 使用示例

### 主窗口与 iframe 通信

**主窗口代码：**

```html
<!DOCTYPE html>
<html>
<head>
  <title>Main Window</title>
</head>
<body>
  <iframe id="editor" src="editor.html"></iframe>
  <script src="https://unpkg.com/@iss-ai/window-message-bus"></script>
  <script>
    const bus = new WindowMessageBus.WindowMessageBus({
      sourceId: 'main-window',
      debug: true,
    });

    // 监听 iframe 消息
    bus.on('editor:message', (data) => {
      console.log('收到 iframe 消息:', data);
    });

    // 向 iframe 发送消息
    setTimeout(() => {
      bus.emitTo('parent', 'init-editor', { config: { theme: 'dark' } });
    }, 1000);
  </script>
</body>
</html>
```

**iframe 代码：**

```html
<!DOCTYPE html>
<html>
<head>
  <title>Iframe Editor</title>
</head>
<body>
  <div id="editor">Editor Content</div>
  <script src="https://unpkg.com/@iss-ai/window-message-bus"></script>
  <script>
    const bus = new WindowMessageBus.WindowMessageBus({
      sourceId: 'iframe-editor',
      debug: true,
    });

    // 监听主窗口消息
    bus.on('init-editor', (data) => {
      console.log('初始化编辑器:', data);
    });

    // 向主窗口发送消息
    bus.emitTo('parent', 'editor:message', { content: 'Hello from iframe!' });
  </script>
</body>
</html>
```

## 脚本命令

```bash
# 构建
npm run build

# 测试
npm test

# 发布
npm run pub

# 覆盖率
npm run coveralls

# TypeScript 检查
npm run tsc
```

## 浏览器支持

- Chrome >= 60
- Firefox >= 55
- Safari >= 11
- Edge >= 79

## 许可证

MIT License

## 贡献

欢迎提交 Issue 和 Pull Request！