@zhyi64/message-bridge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.map +7 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +7 -0
- package/dist/types.d.ts +33 -0
- package/package.json +38 -0
- package/src/MessageBridge.js +153 -0
- package/src/envelope.js +122 -0
- package/src/index.js +3 -0
- package/src/router.js +75 -0
- package/src/types.d.ts +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @zhyi64/message-bridge
|
|
2
|
+
|
|
3
|
+
统一的跨 iframe 通信 SDK,用于多层嵌套 iframe 架构的消息路由与转发。
|
|
4
|
+
|
|
5
|
+
## 架构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
A (onepcweb) ← 根节点
|
|
9
|
+
↑ postMessage
|
|
10
|
+
B (intelligent-web) ← 中间层,自动路由
|
|
11
|
+
↑ postMessage
|
|
12
|
+
C (openclaw) ← 叶节点
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @zhyi64/message-bridge
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 快速开始
|
|
22
|
+
|
|
23
|
+
### 叶节点 (C - openclaw)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { OnePcBridge } from '@zhyi64/message-bridge'
|
|
27
|
+
|
|
28
|
+
const bridge = new OnePcBridge({
|
|
29
|
+
nodeId: 'openclaw',
|
|
30
|
+
allowedOrigins: ['https://...'],
|
|
31
|
+
parentWindow: window.parent,
|
|
32
|
+
debug: true,
|
|
33
|
+
})
|
|
34
|
+
bridge.init()
|
|
35
|
+
|
|
36
|
+
// 直接发送给 A,无需知道中间经过 B
|
|
37
|
+
bridge.send('onepcweb', 'menu', { currentMenu: 'settings' })
|
|
38
|
+
|
|
39
|
+
// 监听来自 A 的消息
|
|
40
|
+
bridge.on('theme', (payload) => {
|
|
41
|
+
document.documentElement.setAttribute('data-theme', payload.theme)
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 中间节点 (B - intelligent-web)
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { OnePcBridge } from '@zhyi64/message-bridge'
|
|
49
|
+
|
|
50
|
+
const bridge = new OnePcBridge({
|
|
51
|
+
nodeId: 'intelligent',
|
|
52
|
+
allowedOrigins: [CHILD_ORIGIN, A_ORIGIN],
|
|
53
|
+
parentWindow: window.parent,
|
|
54
|
+
getChildWindow: () => iframeRef.value?.contentWindow ?? null,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// 注册自动路由 — 一行搞定,无需手动 if/switch 转发
|
|
58
|
+
bridge.addRoute('openclaw', 'onepcweb') // C→A
|
|
59
|
+
bridge.addRoute('onepcweb', 'openclaw') // A→C
|
|
60
|
+
bridge.init()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 根节点 (A - onepcweb)
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { OnePcBridge } from '@zhyi64/message-bridge'
|
|
67
|
+
|
|
68
|
+
const bridge = new OnePcBridge({
|
|
69
|
+
nodeId: 'onepcweb',
|
|
70
|
+
allowedOrigins: ['https://...'],
|
|
71
|
+
getChildWindow: () => document.querySelector('#iframe')?.contentWindow,
|
|
72
|
+
})
|
|
73
|
+
bridge.init()
|
|
74
|
+
|
|
75
|
+
// 直接发送给 C,自动经过 B 中转
|
|
76
|
+
bridge.send('openclaw', 'theme', { theme: 'dark' })
|
|
77
|
+
|
|
78
|
+
// 监听来自 C 的消息
|
|
79
|
+
bridge.on('menu', (payload, envelope) => {
|
|
80
|
+
console.log('来自 C 的菜单消息:', payload)
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## API
|
|
85
|
+
|
|
86
|
+
### `new OnePcBridge(config)`
|
|
87
|
+
|
|
88
|
+
| 参数 | 类型 | 说明 |
|
|
89
|
+
|------|------|------|
|
|
90
|
+
| `nodeId` | `NodeId` | 当前节点标识 |
|
|
91
|
+
| `allowedOrigins` | `string[]` | Origin 白名单 |
|
|
92
|
+
| `parentWindow?` | `Window` | 父窗口引用 |
|
|
93
|
+
| `getChildWindow?` | `() => Window \| null` | 获取子窗口引用的函数 |
|
|
94
|
+
| `debug?` | `boolean` | 开启调试日志 |
|
|
95
|
+
|
|
96
|
+
### 方法
|
|
97
|
+
|
|
98
|
+
| 方法 | 说明 |
|
|
99
|
+
|------|------|
|
|
100
|
+
| `init()` | 注册全局 message 监听 |
|
|
101
|
+
| `destroy()` | 移除监听,清理资源 |
|
|
102
|
+
| `send(target, type, payload?)` | 发送消息(自动路由) |
|
|
103
|
+
| `on(type, handler)` | 注册消息处理器,返回 unsubscribe 函数 |
|
|
104
|
+
| `addRoute(from, to)` | 注册自动路由(中间层使用) |
|
|
105
|
+
| `removeRoute(from, to)` | 移除路由 |
|
|
106
|
+
|
|
107
|
+
## 特性
|
|
108
|
+
|
|
109
|
+
- **自动路由** — 中间层只需 `addRoute()`,无需手动转发
|
|
110
|
+
- **旧格式兼容** — 自动识别新旧消息格式,新旧代码可并行
|
|
111
|
+
- **Origin 安全** — 集中管理白名单
|
|
112
|
+
- **无框架依赖** — 纯 JS,Vue/React/原生均可使用
|
|
113
|
+
- **TypeScript 支持** — 内置类型声明
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var c=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var m=(i,e)=>{for(var t in e)c(i,t,{get:e[t],enumerable:!0})},b=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of p(e))!w.call(i,r)&&r!==t&&c(i,r,{get:()=>e[r],enumerable:!(n=f(e,r))||n.enumerable});return i};var W=i=>b(c({},"__esModule",{value:!0}),i);var R={};m(R,{OnePcBridge:()=>u,Router:()=>o,createEnvelope:()=>a,parseEnvelope:()=>l,serializeEnvelope:()=>s});module.exports=W(R);var y=0;function h(){let i=Date.now().toString(36),e=(++y).toString(36);return`mb_${i}_${e}`}function a(i,e,t,n){return{protocol:"message-bridge-v1",id:h(),from:i,to:e,type:t,payload:n??null,timestamp:Date.now()}}function l(i){if(!i)return null;if(i.protocol==="message-bridge-v1")return i;let e=i;if(e.message&&e.message.subtype==="webToPc"){let t=e.message.data,n;if(typeof t=="string")try{n=JSON.parse(t)}catch{return null}else if(typeof t=="object")n=t;else return null;let{type:r,...d}=n;return r?{protocol:"message-bridge-v1",id:e.id||h(),from:g(e.from),to:E(e.to),type:String(r),payload:d,timestamp:Date.now()}:null}return e.message&&e.message.subtype==="openclawToPc"?{protocol:"message-bridge-v1",id:e.id||h(),from:g(e.from),to:"onepcweb",type:"openclawToPc",payload:e.message.data,timestamp:Date.now()}:null}function g(i){return i?i.includes("openclaw")||i.includes("claw")?"openclaw":i.includes("intelligent")?"intelligent":i.includes("vantage")||i.includes("onepc")?"onepcweb":"intelligent":"intelligent"}function E(i){return i?i.includes("pcWeb")||i.includes("pcMessage")?"onepcweb":i.includes("intelligent")?"intelligent":i.includes("openclaw")||i.includes("claw")?"openclaw":"intelligent":"intelligent"}function s(i){return{...i}}var o=class{constructor(e={}){this.routes=[],this.parentWindow=e.parentWindow??null,this.getChildWindow=e.getChildWindow??null,this.debug=e.debug??!1}addRoute(e,t){this.removeRoute(e,t),this.routes.push({from:e,to:t}),this.log("route registered:",`${e} \u2192 ${t}`)}removeRoute(e,t){this.routes=this.routes.filter(n=>!(n.from===e&&n.to===t))}clearRoutes(){this.routes=[]}getForwardTarget(e){return this.routes.find(n=>n.from===e.from&&n.to===e.to)?e.to==="onepcweb"&&this.parentWindow?this.parentWindow:e.from==="onepcweb"&&this.getChildWindow?this.getChildWindow():e.to!=="intelligent"&&this.parentWindow?this.parentWindow:null:null}forward(e){let t=this.getForwardTarget(e);if(!t)return!1;let n=s(e);return t.postMessage(n,"*"),this.log("forwarded:",`${e.from} \u2192 ${e.to} via postMessage`,e.type),!0}hasRoute(e,t){return this.routes.some(n=>n.from===e&&n.to===t)}process(e){return this.hasRoute(e.from,e.to)?this.forward(e):!1}log(...e){this.debug&&console.log("[OnePcBridge-Router]",...e)}};var u=class{constructor(e){this.config=e,this.handlers=new Map,this.router=new o({parentWindow:e.parentWindow,getChildWindow:e.getChildWindow,debug:e.debug??!1}),this.messageHandler=null,this.initialized=!1}init(){this.initialized||(this.messageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.messageHandler),this.initialized=!0,this.log("initialized",this.config.nodeId))}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.handlers.clear(),this.initialized=!1,this.log("destroyed")}send(e,t,n){let r=a(this.config.nodeId,e,t,n);this.log("send:",`${this.config.nodeId} \u2192 ${e}`,t,n);let d=this.getTargetWindow(e);d?d.postMessage(s(r),"*"):this.warn("no target window for:",e)}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>{let n=this.handlers.get(e);n&&(n.delete(t),n.size===0&&this.handlers.delete(e))}}addRoute(e,t){this.router?.addRoute(e,t)}removeRoute(e,t){this.router?.removeRoute(e,t)}handleMessage(e){if(e.origin&&!this.config.allowedOrigins.includes(e.origin))return;let t=l(e.data);if(t&&!(t.from===this.config.nodeId&&t.to===this.config.nodeId)){if(t.to!==this.config.nodeId){if(this.router?.process(t)){this.dispatch(t);return}return}this.dispatch(t)}}dispatch(e){let t=this.handlers.get(e.type);if(t)for(let n of t)try{n(e.payload,e)}catch(r){this.warn("handler error:",e.type,r)}}getTargetWindow(e){return this.config.parentWindow&&e!==this.config.nodeId?this.config.parentWindow:this.config.getChildWindow?this.config.getChildWindow():null}log(...e){this.config.debug&&console.log(`[OnePcBridge:${this.config.nodeId}]`,...e)}warn(...e){console.warn(`[OnePcBridge:${this.config.nodeId}]`,...e)}};
|
|
2
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js", "../src/envelope.js", "../src/router.js", "../src/MessageBridge.js"],
|
|
4
|
+
"sourcesContent": ["export { OnePcBridge } from './MessageBridge'\nexport { createEnvelope, parseEnvelope, serializeEnvelope } from './envelope'\nexport { Router } from './router'\n", "let _counter = 0\n\n/**\n * Generate a lightweight unique ID without external dependencies.\n * Uses timestamp + counter for uniqueness within a session.\n */\nfunction generateId() {\n const ts = Date.now().toString(36)\n const seq = (++_counter).toString(36)\n return `mb_${ts}_${seq}`\n}\n\n/**\n * Create a new message envelope (new protocol format).\n */\nexport function createEnvelope(from, to, type, payload) {\n return {\n protocol: 'message-bridge-v1',\n id: generateId(),\n from,\n to,\n type,\n payload: payload ?? null,\n timestamp: Date.now(),\n }\n}\n\n/**\n * Parse a raw postMessage event into an Envelope.\n * Supports both the new protocol format and the legacy format\n * for backward compatibility during migration.\n *\n * New format:\n * { protocol: 'message-bridge-v1', from, to, type, payload, ... }\n *\n * Legacy format:\n * { to, from, id, message: { subtype: 'webToPc', data: JSON.stringify({type, ...}) } }\n */\nexport function parseEnvelope(raw) {\n if (!raw) return null\n\n // --- New protocol ---\n if (raw.protocol === 'message-bridge-v1') {\n return raw\n }\n\n // --- Legacy format detection ---\n const legacy = raw\n if (legacy.message && legacy.message.subtype === 'webToPc') {\n const data = legacy.message.data\n let parsed\n\n if (typeof data === 'string') {\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n } else if (typeof data === 'object') {\n parsed = data\n } else {\n return null\n }\n\n const { type, ...restPayload } = parsed\n if (!type) return null\n\n return {\n protocol: 'message-bridge-v1',\n id: legacy.id || generateId(),\n from: detectFromNode(legacy.from),\n to: detectToNode(legacy.to),\n type: String(type),\n payload: restPayload,\n timestamp: Date.now(),\n }\n }\n\n // Legacy: openclawToPc subtype\n if (legacy.message && legacy.message.subtype === 'openclawToPc') {\n return {\n protocol: 'message-bridge-v1',\n id: legacy.id || generateId(),\n from: detectFromNode(legacy.from),\n to: 'onepcweb',\n type: 'openclawToPc',\n payload: legacy.message.data,\n timestamp: Date.now(),\n }\n }\n\n return null\n}\n\n/**\n * Detect the source NodeId from legacy message's `from` field.\n */\nfunction detectFromNode(from) {\n if (!from) return 'intelligent'\n if (from.includes('openclaw') || from.includes('claw')) return 'openclaw'\n if (from.includes('intelligent')) return 'intelligent'\n if (from.includes('vantage') || from.includes('onepc')) return 'onepcweb'\n return 'intelligent'\n}\n\n/**\n * Detect the target NodeId from legacy message's `to` field.\n */\nfunction detectToNode(to) {\n if (!to) return 'intelligent'\n if (to.includes('pcWeb') || to.includes('pcMessage')) return 'onepcweb'\n if (to.includes('intelligent')) return 'intelligent'\n if (to.includes('openclaw') || to.includes('claw')) return 'openclaw'\n return 'intelligent'\n}\n\n/**\n * Serialize an Envelope for postMessage transmission.\n */\nexport function serializeEnvelope(envelope) {\n return { ...envelope }\n}\n", "import { createEnvelope, serializeEnvelope } from './envelope'\n\n/**\n * Router manages automatic message forwarding for middle-layer nodes.\n */\nexport class Router {\n constructor(opts = {}) {\n this.routes = []\n this.parentWindow = opts.parentWindow ?? null\n this.getChildWindow = opts.getChildWindow ?? null\n this.debug = opts.debug ?? false\n }\n\n addRoute(from, to) {\n this.removeRoute(from, to)\n this.routes.push({ from, to })\n this.log('route registered:', `${from} \u2192 ${to}`)\n }\n\n removeRoute(from, to) {\n this.routes = this.routes.filter(\n r => !(r.from === from && r.to === to)\n )\n }\n\n clearRoutes() {\n this.routes = []\n }\n\n getForwardTarget(envelope) {\n const route = this.routes.find(\n r => r.from === envelope.from && r.to === envelope.to\n )\n if (!route) return null\n\n if (envelope.to === 'onepcweb' && this.parentWindow) {\n return this.parentWindow\n }\n if (envelope.from === 'onepcweb' && this.getChildWindow) {\n return this.getChildWindow()\n }\n if (envelope.to !== 'intelligent' && this.parentWindow) {\n return this.parentWindow\n }\n\n return null\n }\n\n forward(envelope) {\n const target = this.getForwardTarget(envelope)\n if (!target) return false\n\n const body = serializeEnvelope(envelope)\n target.postMessage(body, '*')\n this.log('forwarded:', `${envelope.from} \u2192 ${envelope.to} via postMessage`, envelope.type)\n return true\n }\n\n hasRoute(from, to) {\n return this.routes.some(r => r.from === from && r.to === to)\n }\n\n process(envelope) {\n if (this.hasRoute(envelope.from, envelope.to)) {\n return this.forward(envelope)\n }\n return false\n }\n\n log(...args) {\n if (this.debug) {\n console.log('[OnePcBridge-Router]', ...args)\n }\n }\n}\n", "import { createEnvelope, parseEnvelope, serializeEnvelope } from './envelope'\nimport { Router } from './router'\n\n/**\n * OnePcBridge \u2014 unified cross-iframe communication SDK.\n *\n * Usage:\n * // Leaf node (C - openclaw)\n * const bridge = new OnePcBridge({\n * nodeId: 'openclaw',\n * allowedOrigins: ['https://...'],\n * parentWindow: window.parent,\n * })\n * bridge.init()\n * bridge.send('onepcweb', 'menu', { data: '...' })\n *\n * // Middle node (B - intelligent-web)\n * const bridge = new OnePcBridge({\n * nodeId: 'intelligent',\n * allowedOrigins: ['https://...', CHILD_ORIGIN],\n * parentWindow: window.parent,\n * getChildWindow: () => iframeRef.value?.contentWindow,\n * })\n * bridge.addRoute('openclaw', 'onepcweb')\n * bridge.init()\n */\nexport class OnePcBridge {\n constructor(config) {\n this.config = config\n this.handlers = new Map()\n this.router = new Router({\n parentWindow: config.parentWindow,\n getChildWindow: config.getChildWindow,\n debug: config.debug ?? false,\n })\n this.messageHandler = null\n this.initialized = false\n }\n\n init() {\n if (this.initialized) return\n\n this.messageHandler = this.handleMessage.bind(this)\n window.addEventListener('message', this.messageHandler)\n this.initialized = true\n this.log('initialized', this.config.nodeId)\n }\n\n destroy() {\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler)\n this.messageHandler = null\n }\n this.handlers.clear()\n this.initialized = false\n this.log('destroyed')\n }\n\n send(target, type, payload) {\n const envelope = createEnvelope(this.config.nodeId, target, type, payload)\n this.log('send:', `${this.config.nodeId} \u2192 ${target}`, type, payload)\n\n const targetWindow = this.getTargetWindow(target)\n if (targetWindow) {\n targetWindow.postMessage(serializeEnvelope(envelope), '*')\n } else {\n this.warn('no target window for:', target)\n }\n }\n\n on(type, handler) {\n if (!this.handlers.has(type)) {\n this.handlers.set(type, new Set())\n }\n this.handlers.get(type).add(handler)\n\n return () => {\n const set = this.handlers.get(type)\n if (set) {\n set.delete(handler)\n if (set.size === 0) {\n this.handlers.delete(type)\n }\n }\n }\n }\n\n addRoute(from, to) {\n this.router?.addRoute(from, to)\n }\n\n removeRoute(from, to) {\n this.router?.removeRoute(from, to)\n }\n\n // \u2500\u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n handleMessage(event) {\n if (event.origin && !this.config.allowedOrigins.includes(event.origin)) {\n return\n }\n\n const envelope = parseEnvelope(event.data)\n if (!envelope) return\n\n if (envelope.from === this.config.nodeId && envelope.to === this.config.nodeId) {\n return\n }\n\n if (envelope.to !== this.config.nodeId) {\n if (this.router?.process(envelope)) {\n this.dispatch(envelope)\n return\n }\n return\n }\n\n this.dispatch(envelope)\n }\n\n dispatch(envelope) {\n const typeHandlers = this.handlers.get(envelope.type)\n if (typeHandlers) {\n for (const handler of typeHandlers) {\n try {\n handler(envelope.payload, envelope)\n } catch (err) {\n this.warn('handler error:', envelope.type, err)\n }\n }\n }\n }\n\n getTargetWindow(target) {\n if (this.config.parentWindow && target !== this.config.nodeId) {\n return this.config.parentWindow\n }\n if (this.config.getChildWindow) {\n return this.config.getChildWindow()\n }\n return null\n }\n\n log(...args) {\n if (this.config.debug) {\n console.log(`[OnePcBridge:${this.config.nodeId}]`, ...args)\n }\n }\n\n warn(...args) {\n console.warn(`[OnePcBridge:${this.config.nodeId}]`, ...args)\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["index_exports", "__export", "OnePcBridge", "Router", "createEnvelope", "parseEnvelope", "serializeEnvelope", "__toCommonJS", "_counter", "generateId", "ts", "seq", "createEnvelope", "from", "to", "type", "payload", "parseEnvelope", "raw", "legacy", "data", "parsed", "restPayload", "detectFromNode", "detectToNode", "serializeEnvelope", "envelope", "Router", "opts", "from", "to", "r", "envelope", "target", "body", "serializeEnvelope", "args", "OnePcBridge", "config", "Router", "target", "type", "payload", "envelope", "createEnvelope", "targetWindow", "serializeEnvelope", "handler", "set", "from", "to", "event", "parseEnvelope", "typeHandlers", "err", "args"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var g=0;function a(){let i=Date.now().toString(36),e=(++g).toString(36);return`mb_${i}_${e}`}function l(i,e,t,n){return{protocol:"message-bridge-v1",id:a(),from:i,to:e,type:t,payload:n??null,timestamp:Date.now()}}function u(i){if(!i)return null;if(i.protocol==="message-bridge-v1")return i;let e=i;if(e.message&&e.message.subtype==="webToPc"){let t=e.message.data,n;if(typeof t=="string")try{n=JSON.parse(t)}catch{return null}else if(typeof t=="object")n=t;else return null;let{type:r,...d}=n;return r?{protocol:"message-bridge-v1",id:e.id||a(),from:h(e.from),to:f(e.to),type:String(r),payload:d,timestamp:Date.now()}:null}return e.message&&e.message.subtype==="openclawToPc"?{protocol:"message-bridge-v1",id:e.id||a(),from:h(e.from),to:"onepcweb",type:"openclawToPc",payload:e.message.data,timestamp:Date.now()}:null}function h(i){return i?i.includes("openclaw")||i.includes("claw")?"openclaw":i.includes("intelligent")?"intelligent":i.includes("vantage")||i.includes("onepc")?"onepcweb":"intelligent":"intelligent"}function f(i){return i?i.includes("pcWeb")||i.includes("pcMessage")?"onepcweb":i.includes("intelligent")?"intelligent":i.includes("openclaw")||i.includes("claw")?"openclaw":"intelligent":"intelligent"}function s(i){return{...i}}var o=class{constructor(e={}){this.routes=[],this.parentWindow=e.parentWindow??null,this.getChildWindow=e.getChildWindow??null,this.debug=e.debug??!1}addRoute(e,t){this.removeRoute(e,t),this.routes.push({from:e,to:t}),this.log("route registered:",`${e} \u2192 ${t}`)}removeRoute(e,t){this.routes=this.routes.filter(n=>!(n.from===e&&n.to===t))}clearRoutes(){this.routes=[]}getForwardTarget(e){return this.routes.find(n=>n.from===e.from&&n.to===e.to)?e.to==="onepcweb"&&this.parentWindow?this.parentWindow:e.from==="onepcweb"&&this.getChildWindow?this.getChildWindow():e.to!=="intelligent"&&this.parentWindow?this.parentWindow:null:null}forward(e){let t=this.getForwardTarget(e);if(!t)return!1;let n=s(e);return t.postMessage(n,"*"),this.log("forwarded:",`${e.from} \u2192 ${e.to} via postMessage`,e.type),!0}hasRoute(e,t){return this.routes.some(n=>n.from===e&&n.to===t)}process(e){return this.hasRoute(e.from,e.to)?this.forward(e):!1}log(...e){this.debug&&console.log("[OnePcBridge-Router]",...e)}};var c=class{constructor(e){this.config=e,this.handlers=new Map,this.router=new o({parentWindow:e.parentWindow,getChildWindow:e.getChildWindow,debug:e.debug??!1}),this.messageHandler=null,this.initialized=!1}init(){this.initialized||(this.messageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.messageHandler),this.initialized=!0,this.log("initialized",this.config.nodeId))}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.handlers.clear(),this.initialized=!1,this.log("destroyed")}send(e,t,n){let r=l(this.config.nodeId,e,t,n);this.log("send:",`${this.config.nodeId} \u2192 ${e}`,t,n);let d=this.getTargetWindow(e);d?d.postMessage(s(r),"*"):this.warn("no target window for:",e)}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>{let n=this.handlers.get(e);n&&(n.delete(t),n.size===0&&this.handlers.delete(e))}}addRoute(e,t){this.router?.addRoute(e,t)}removeRoute(e,t){this.router?.removeRoute(e,t)}handleMessage(e){if(e.origin&&!this.config.allowedOrigins.includes(e.origin))return;let t=u(e.data);if(t&&!(t.from===this.config.nodeId&&t.to===this.config.nodeId)){if(t.to!==this.config.nodeId){if(this.router?.process(t)){this.dispatch(t);return}return}this.dispatch(t)}}dispatch(e){let t=this.handlers.get(e.type);if(t)for(let n of t)try{n(e.payload,e)}catch(r){this.warn("handler error:",e.type,r)}}getTargetWindow(e){return this.config.parentWindow&&e!==this.config.nodeId?this.config.parentWindow:this.config.getChildWindow?this.config.getChildWindow():null}log(...e){this.config.debug&&console.log(`[OnePcBridge:${this.config.nodeId}]`,...e)}warn(...e){console.warn(`[OnePcBridge:${this.config.nodeId}]`,...e)}};export{c as OnePcBridge,o as Router,l as createEnvelope,u as parseEnvelope,s as serializeEnvelope};
|
|
2
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/envelope.js", "../src/router.js", "../src/MessageBridge.js"],
|
|
4
|
+
"sourcesContent": ["let _counter = 0\n\n/**\n * Generate a lightweight unique ID without external dependencies.\n * Uses timestamp + counter for uniqueness within a session.\n */\nfunction generateId() {\n const ts = Date.now().toString(36)\n const seq = (++_counter).toString(36)\n return `mb_${ts}_${seq}`\n}\n\n/**\n * Create a new message envelope (new protocol format).\n */\nexport function createEnvelope(from, to, type, payload) {\n return {\n protocol: 'message-bridge-v1',\n id: generateId(),\n from,\n to,\n type,\n payload: payload ?? null,\n timestamp: Date.now(),\n }\n}\n\n/**\n * Parse a raw postMessage event into an Envelope.\n * Supports both the new protocol format and the legacy format\n * for backward compatibility during migration.\n *\n * New format:\n * { protocol: 'message-bridge-v1', from, to, type, payload, ... }\n *\n * Legacy format:\n * { to, from, id, message: { subtype: 'webToPc', data: JSON.stringify({type, ...}) } }\n */\nexport function parseEnvelope(raw) {\n if (!raw) return null\n\n // --- New protocol ---\n if (raw.protocol === 'message-bridge-v1') {\n return raw\n }\n\n // --- Legacy format detection ---\n const legacy = raw\n if (legacy.message && legacy.message.subtype === 'webToPc') {\n const data = legacy.message.data\n let parsed\n\n if (typeof data === 'string') {\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n } else if (typeof data === 'object') {\n parsed = data\n } else {\n return null\n }\n\n const { type, ...restPayload } = parsed\n if (!type) return null\n\n return {\n protocol: 'message-bridge-v1',\n id: legacy.id || generateId(),\n from: detectFromNode(legacy.from),\n to: detectToNode(legacy.to),\n type: String(type),\n payload: restPayload,\n timestamp: Date.now(),\n }\n }\n\n // Legacy: openclawToPc subtype\n if (legacy.message && legacy.message.subtype === 'openclawToPc') {\n return {\n protocol: 'message-bridge-v1',\n id: legacy.id || generateId(),\n from: detectFromNode(legacy.from),\n to: 'onepcweb',\n type: 'openclawToPc',\n payload: legacy.message.data,\n timestamp: Date.now(),\n }\n }\n\n return null\n}\n\n/**\n * Detect the source NodeId from legacy message's `from` field.\n */\nfunction detectFromNode(from) {\n if (!from) return 'intelligent'\n if (from.includes('openclaw') || from.includes('claw')) return 'openclaw'\n if (from.includes('intelligent')) return 'intelligent'\n if (from.includes('vantage') || from.includes('onepc')) return 'onepcweb'\n return 'intelligent'\n}\n\n/**\n * Detect the target NodeId from legacy message's `to` field.\n */\nfunction detectToNode(to) {\n if (!to) return 'intelligent'\n if (to.includes('pcWeb') || to.includes('pcMessage')) return 'onepcweb'\n if (to.includes('intelligent')) return 'intelligent'\n if (to.includes('openclaw') || to.includes('claw')) return 'openclaw'\n return 'intelligent'\n}\n\n/**\n * Serialize an Envelope for postMessage transmission.\n */\nexport function serializeEnvelope(envelope) {\n return { ...envelope }\n}\n", "import { createEnvelope, serializeEnvelope } from './envelope'\n\n/**\n * Router manages automatic message forwarding for middle-layer nodes.\n */\nexport class Router {\n constructor(opts = {}) {\n this.routes = []\n this.parentWindow = opts.parentWindow ?? null\n this.getChildWindow = opts.getChildWindow ?? null\n this.debug = opts.debug ?? false\n }\n\n addRoute(from, to) {\n this.removeRoute(from, to)\n this.routes.push({ from, to })\n this.log('route registered:', `${from} \u2192 ${to}`)\n }\n\n removeRoute(from, to) {\n this.routes = this.routes.filter(\n r => !(r.from === from && r.to === to)\n )\n }\n\n clearRoutes() {\n this.routes = []\n }\n\n getForwardTarget(envelope) {\n const route = this.routes.find(\n r => r.from === envelope.from && r.to === envelope.to\n )\n if (!route) return null\n\n if (envelope.to === 'onepcweb' && this.parentWindow) {\n return this.parentWindow\n }\n if (envelope.from === 'onepcweb' && this.getChildWindow) {\n return this.getChildWindow()\n }\n if (envelope.to !== 'intelligent' && this.parentWindow) {\n return this.parentWindow\n }\n\n return null\n }\n\n forward(envelope) {\n const target = this.getForwardTarget(envelope)\n if (!target) return false\n\n const body = serializeEnvelope(envelope)\n target.postMessage(body, '*')\n this.log('forwarded:', `${envelope.from} \u2192 ${envelope.to} via postMessage`, envelope.type)\n return true\n }\n\n hasRoute(from, to) {\n return this.routes.some(r => r.from === from && r.to === to)\n }\n\n process(envelope) {\n if (this.hasRoute(envelope.from, envelope.to)) {\n return this.forward(envelope)\n }\n return false\n }\n\n log(...args) {\n if (this.debug) {\n console.log('[OnePcBridge-Router]', ...args)\n }\n }\n}\n", "import { createEnvelope, parseEnvelope, serializeEnvelope } from './envelope'\nimport { Router } from './router'\n\n/**\n * OnePcBridge \u2014 unified cross-iframe communication SDK.\n *\n * Usage:\n * // Leaf node (C - openclaw)\n * const bridge = new OnePcBridge({\n * nodeId: 'openclaw',\n * allowedOrigins: ['https://...'],\n * parentWindow: window.parent,\n * })\n * bridge.init()\n * bridge.send('onepcweb', 'menu', { data: '...' })\n *\n * // Middle node (B - intelligent-web)\n * const bridge = new OnePcBridge({\n * nodeId: 'intelligent',\n * allowedOrigins: ['https://...', CHILD_ORIGIN],\n * parentWindow: window.parent,\n * getChildWindow: () => iframeRef.value?.contentWindow,\n * })\n * bridge.addRoute('openclaw', 'onepcweb')\n * bridge.init()\n */\nexport class OnePcBridge {\n constructor(config) {\n this.config = config\n this.handlers = new Map()\n this.router = new Router({\n parentWindow: config.parentWindow,\n getChildWindow: config.getChildWindow,\n debug: config.debug ?? false,\n })\n this.messageHandler = null\n this.initialized = false\n }\n\n init() {\n if (this.initialized) return\n\n this.messageHandler = this.handleMessage.bind(this)\n window.addEventListener('message', this.messageHandler)\n this.initialized = true\n this.log('initialized', this.config.nodeId)\n }\n\n destroy() {\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler)\n this.messageHandler = null\n }\n this.handlers.clear()\n this.initialized = false\n this.log('destroyed')\n }\n\n send(target, type, payload) {\n const envelope = createEnvelope(this.config.nodeId, target, type, payload)\n this.log('send:', `${this.config.nodeId} \u2192 ${target}`, type, payload)\n\n const targetWindow = this.getTargetWindow(target)\n if (targetWindow) {\n targetWindow.postMessage(serializeEnvelope(envelope), '*')\n } else {\n this.warn('no target window for:', target)\n }\n }\n\n on(type, handler) {\n if (!this.handlers.has(type)) {\n this.handlers.set(type, new Set())\n }\n this.handlers.get(type).add(handler)\n\n return () => {\n const set = this.handlers.get(type)\n if (set) {\n set.delete(handler)\n if (set.size === 0) {\n this.handlers.delete(type)\n }\n }\n }\n }\n\n addRoute(from, to) {\n this.router?.addRoute(from, to)\n }\n\n removeRoute(from, to) {\n this.router?.removeRoute(from, to)\n }\n\n // \u2500\u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n handleMessage(event) {\n if (event.origin && !this.config.allowedOrigins.includes(event.origin)) {\n return\n }\n\n const envelope = parseEnvelope(event.data)\n if (!envelope) return\n\n if (envelope.from === this.config.nodeId && envelope.to === this.config.nodeId) {\n return\n }\n\n if (envelope.to !== this.config.nodeId) {\n if (this.router?.process(envelope)) {\n this.dispatch(envelope)\n return\n }\n return\n }\n\n this.dispatch(envelope)\n }\n\n dispatch(envelope) {\n const typeHandlers = this.handlers.get(envelope.type)\n if (typeHandlers) {\n for (const handler of typeHandlers) {\n try {\n handler(envelope.payload, envelope)\n } catch (err) {\n this.warn('handler error:', envelope.type, err)\n }\n }\n }\n }\n\n getTargetWindow(target) {\n if (this.config.parentWindow && target !== this.config.nodeId) {\n return this.config.parentWindow\n }\n if (this.config.getChildWindow) {\n return this.config.getChildWindow()\n }\n return null\n }\n\n log(...args) {\n if (this.config.debug) {\n console.log(`[OnePcBridge:${this.config.nodeId}]`, ...args)\n }\n }\n\n warn(...args) {\n console.warn(`[OnePcBridge:${this.config.nodeId}]`, ...args)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,IAAIA,EAAW,EAMf,SAASC,GAAa,CACpB,IAAMC,EAAK,KAAK,IAAI,EAAE,SAAS,EAAE,EAC3BC,GAAO,EAAEH,GAAU,SAAS,EAAE,EACpC,MAAO,MAAME,CAAE,IAAIC,CAAG,EACxB,CAKO,SAASC,EAAeC,EAAMC,EAAIC,EAAMC,EAAS,CACtD,MAAO,CACL,SAAU,oBACV,GAAIP,EAAW,EACf,KAAAI,EACA,GAAAC,EACA,KAAAC,EACA,QAASC,GAAW,KACpB,UAAW,KAAK,IAAI,CACtB,CACF,CAaO,SAASC,EAAcC,EAAK,CACjC,GAAI,CAACA,EAAK,OAAO,KAGjB,GAAIA,EAAI,WAAa,oBACnB,OAAOA,EAIT,IAAMC,EAASD,EACf,GAAIC,EAAO,SAAWA,EAAO,QAAQ,UAAY,UAAW,CAC1D,IAAMC,EAAOD,EAAO,QAAQ,KACxBE,EAEJ,GAAI,OAAOD,GAAS,SAClB,GAAI,CACFC,EAAS,KAAK,MAAMD,CAAI,CAC1B,MAAQ,CACN,OAAO,IACT,SACS,OAAOA,GAAS,SACzBC,EAASD,MAET,QAAO,KAGT,GAAM,CAAE,KAAAL,EAAM,GAAGO,CAAY,EAAID,EACjC,OAAKN,EAEE,CACL,SAAU,oBACV,GAAII,EAAO,IAAMV,EAAW,EAC5B,KAAMc,EAAeJ,EAAO,IAAI,EAChC,GAAIK,EAAaL,EAAO,EAAE,EAC1B,KAAM,OAAOJ,CAAI,EACjB,QAASO,EACT,UAAW,KAAK,IAAI,CACtB,EAVkB,IAWpB,CAGA,OAAIH,EAAO,SAAWA,EAAO,QAAQ,UAAY,eACxC,CACL,SAAU,oBACV,GAAIA,EAAO,IAAMV,EAAW,EAC5B,KAAMc,EAAeJ,EAAO,IAAI,EAChC,GAAI,WACJ,KAAM,eACN,QAASA,EAAO,QAAQ,KACxB,UAAW,KAAK,IAAI,CACtB,EAGK,IACT,CAKA,SAASI,EAAeV,EAAM,CAC5B,OAAKA,EACDA,EAAK,SAAS,UAAU,GAAKA,EAAK,SAAS,MAAM,EAAU,WAC3DA,EAAK,SAAS,aAAa,EAAU,cACrCA,EAAK,SAAS,SAAS,GAAKA,EAAK,SAAS,OAAO,EAAU,WACxD,cAJW,aAKpB,CAKA,SAASW,EAAaV,EAAI,CACxB,OAAKA,EACDA,EAAG,SAAS,OAAO,GAAKA,EAAG,SAAS,WAAW,EAAU,WACzDA,EAAG,SAAS,aAAa,EAAU,cACnCA,EAAG,SAAS,UAAU,GAAKA,EAAG,SAAS,MAAM,EAAU,WACpD,cAJS,aAKlB,CAKO,SAASW,EAAkBC,EAAU,CAC1C,MAAO,CAAE,GAAGA,CAAS,CACvB,CCpHO,IAAMC,EAAN,KAAa,CAClB,YAAYC,EAAO,CAAC,EAAG,CACrB,KAAK,OAAS,CAAC,EACf,KAAK,aAAeA,EAAK,cAAgB,KACzC,KAAK,eAAiBA,EAAK,gBAAkB,KAC7C,KAAK,MAAQA,EAAK,OAAS,EAC7B,CAEA,SAASC,EAAMC,EAAI,CACjB,KAAK,YAAYD,EAAMC,CAAE,EACzB,KAAK,OAAO,KAAK,CAAE,KAAAD,EAAM,GAAAC,CAAG,CAAC,EAC7B,KAAK,IAAI,oBAAqB,GAAGD,CAAI,WAAMC,CAAE,EAAE,CACjD,CAEA,YAAYD,EAAMC,EAAI,CACpB,KAAK,OAAS,KAAK,OAAO,OACxBC,GAAK,EAAEA,EAAE,OAASF,GAAQE,EAAE,KAAOD,EACrC,CACF,CAEA,aAAc,CACZ,KAAK,OAAS,CAAC,CACjB,CAEA,iBAAiBE,EAAU,CAIzB,OAHc,KAAK,OAAO,KACxBD,GAAKA,EAAE,OAASC,EAAS,MAAQD,EAAE,KAAOC,EAAS,EACrD,EAGIA,EAAS,KAAO,YAAc,KAAK,aAC9B,KAAK,aAEVA,EAAS,OAAS,YAAc,KAAK,eAChC,KAAK,eAAe,EAEzBA,EAAS,KAAO,eAAiB,KAAK,aACjC,KAAK,aAGP,KAZY,IAarB,CAEA,QAAQA,EAAU,CAChB,IAAMC,EAAS,KAAK,iBAAiBD,CAAQ,EAC7C,GAAI,CAACC,EAAQ,MAAO,GAEpB,IAAMC,EAAOC,EAAkBH,CAAQ,EACvC,OAAAC,EAAO,YAAYC,EAAM,GAAG,EAC5B,KAAK,IAAI,aAAc,GAAGF,EAAS,IAAI,WAAMA,EAAS,EAAE,mBAAoBA,EAAS,IAAI,EAClF,EACT,CAEA,SAASH,EAAMC,EAAI,CACjB,OAAO,KAAK,OAAO,KAAKC,GAAKA,EAAE,OAASF,GAAQE,EAAE,KAAOD,CAAE,CAC7D,CAEA,QAAQE,EAAU,CAChB,OAAI,KAAK,SAASA,EAAS,KAAMA,EAAS,EAAE,EACnC,KAAK,QAAQA,CAAQ,EAEvB,EACT,CAEA,OAAOI,EAAM,CACP,KAAK,OACP,QAAQ,IAAI,uBAAwB,GAAGA,CAAI,CAE/C,CACF,EChDO,IAAMC,EAAN,KAAkB,CACvB,YAAYC,EAAQ,CAClB,KAAK,OAASA,EACd,KAAK,SAAW,IAAI,IACpB,KAAK,OAAS,IAAIC,EAAO,CACvB,aAAcD,EAAO,aACrB,eAAgBA,EAAO,eACvB,MAAOA,EAAO,OAAS,EACzB,CAAC,EACD,KAAK,eAAiB,KACtB,KAAK,YAAc,EACrB,CAEA,MAAO,CACD,KAAK,cAET,KAAK,eAAiB,KAAK,cAAc,KAAK,IAAI,EAClD,OAAO,iBAAiB,UAAW,KAAK,cAAc,EACtD,KAAK,YAAc,GACnB,KAAK,IAAI,cAAe,KAAK,OAAO,MAAM,EAC5C,CAEA,SAAU,CACJ,KAAK,iBACP,OAAO,oBAAoB,UAAW,KAAK,cAAc,EACzD,KAAK,eAAiB,MAExB,KAAK,SAAS,MAAM,EACpB,KAAK,YAAc,GACnB,KAAK,IAAI,WAAW,CACtB,CAEA,KAAKE,EAAQC,EAAMC,EAAS,CAC1B,IAAMC,EAAWC,EAAe,KAAK,OAAO,OAAQJ,EAAQC,EAAMC,CAAO,EACzE,KAAK,IAAI,QAAS,GAAG,KAAK,OAAO,MAAM,WAAMF,CAAM,GAAIC,EAAMC,CAAO,EAEpE,IAAMG,EAAe,KAAK,gBAAgBL,CAAM,EAC5CK,EACFA,EAAa,YAAYC,EAAkBH,CAAQ,EAAG,GAAG,EAEzD,KAAK,KAAK,wBAAyBH,CAAM,CAE7C,CAEA,GAAGC,EAAMM,EAAS,CAChB,OAAK,KAAK,SAAS,IAAIN,CAAI,GACzB,KAAK,SAAS,IAAIA,EAAM,IAAI,GAAK,EAEnC,KAAK,SAAS,IAAIA,CAAI,EAAE,IAAIM,CAAO,EAE5B,IAAM,CACX,IAAMC,EAAM,KAAK,SAAS,IAAIP,CAAI,EAC9BO,IACFA,EAAI,OAAOD,CAAO,EACdC,EAAI,OAAS,GACf,KAAK,SAAS,OAAOP,CAAI,EAG/B,CACF,CAEA,SAASQ,EAAMC,EAAI,CACjB,KAAK,QAAQ,SAASD,EAAMC,CAAE,CAChC,CAEA,YAAYD,EAAMC,EAAI,CACpB,KAAK,QAAQ,YAAYD,EAAMC,CAAE,CACnC,CAIA,cAAcC,EAAO,CACnB,GAAIA,EAAM,QAAU,CAAC,KAAK,OAAO,eAAe,SAASA,EAAM,MAAM,EACnE,OAGF,IAAMR,EAAWS,EAAcD,EAAM,IAAI,EACzC,GAAKR,GAED,EAAAA,EAAS,OAAS,KAAK,OAAO,QAAUA,EAAS,KAAO,KAAK,OAAO,QAIxE,IAAIA,EAAS,KAAO,KAAK,OAAO,OAAQ,CACtC,GAAI,KAAK,QAAQ,QAAQA,CAAQ,EAAG,CAClC,KAAK,SAASA,CAAQ,EACtB,MACF,CACA,MACF,CAEA,KAAK,SAASA,CAAQ,EACxB,CAEA,SAASA,EAAU,CACjB,IAAMU,EAAe,KAAK,SAAS,IAAIV,EAAS,IAAI,EACpD,GAAIU,EACF,QAAWN,KAAWM,EACpB,GAAI,CACFN,EAAQJ,EAAS,QAASA,CAAQ,CACpC,OAASW,EAAK,CACZ,KAAK,KAAK,iBAAkBX,EAAS,KAAMW,CAAG,CAChD,CAGN,CAEA,gBAAgBd,EAAQ,CACtB,OAAI,KAAK,OAAO,cAAgBA,IAAW,KAAK,OAAO,OAC9C,KAAK,OAAO,aAEjB,KAAK,OAAO,eACP,KAAK,OAAO,eAAe,EAE7B,IACT,CAEA,OAAOe,EAAM,CACP,KAAK,OAAO,OACd,QAAQ,IAAI,gBAAgB,KAAK,OAAO,MAAM,IAAK,GAAGA,CAAI,CAE9D,CAEA,QAAQA,EAAM,CACZ,QAAQ,KAAK,gBAAgB,KAAK,OAAO,MAAM,IAAK,GAAGA,CAAI,CAC7D,CACF",
|
|
6
|
+
"names": ["_counter", "generateId", "ts", "seq", "createEnvelope", "from", "to", "type", "payload", "parseEnvelope", "raw", "legacy", "data", "parsed", "restPayload", "detectFromNode", "detectToNode", "serializeEnvelope", "envelope", "Router", "opts", "from", "to", "r", "envelope", "target", "body", "serializeEnvelope", "args", "OnePcBridge", "config", "Router", "target", "type", "payload", "envelope", "createEnvelope", "targetWindow", "serializeEnvelope", "handler", "set", "from", "to", "event", "parseEnvelope", "typeHandlers", "err", "args"]
|
|
7
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type NodeId = 'onepcweb' | 'intelligent' | 'openclaw'
|
|
2
|
+
|
|
3
|
+
export interface Envelope {
|
|
4
|
+
protocol: 'message-bridge-v1'
|
|
5
|
+
id: string
|
|
6
|
+
from: NodeId
|
|
7
|
+
to: NodeId
|
|
8
|
+
type: string
|
|
9
|
+
payload: any
|
|
10
|
+
timestamp: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Config {
|
|
14
|
+
nodeId: NodeId
|
|
15
|
+
allowedOrigins: string[]
|
|
16
|
+
parentWindow?: Window
|
|
17
|
+
getChildWindow?: () => Window | null
|
|
18
|
+
debug?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Unsubscribe = () => void
|
|
22
|
+
|
|
23
|
+
export type MessageHandler = (payload: any, envelope: Envelope) => void
|
|
24
|
+
|
|
25
|
+
export class OnePcBridge {
|
|
26
|
+
constructor(config: Config)
|
|
27
|
+
init(): void
|
|
28
|
+
destroy(): void
|
|
29
|
+
send(target: NodeId, type: string, payload?: any): void
|
|
30
|
+
on(type: string, handler: MessageHandler): Unsubscribe
|
|
31
|
+
addRoute(from: NodeId, to: NodeId): void
|
|
32
|
+
removeRoute(from: NodeId, to: NodeId): void
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhyi64/message-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Unified cross-iframe communication SDK for nested iframe architecture (onepcweb → intelligent-web → openclaw)",
|
|
5
|
+
"main": "dist/index.cjs.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/types.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.esm.js",
|
|
11
|
+
"require": "./dist/index.cjs.js",
|
|
12
|
+
"types": "./dist/types.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "node build.js",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"iframe",
|
|
26
|
+
"postMessage",
|
|
27
|
+
"bridge",
|
|
28
|
+
"communication",
|
|
29
|
+
"onepcweb"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"esbuild": "^0.28.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createEnvelope, parseEnvelope, serializeEnvelope } from './envelope'
|
|
2
|
+
import { Router } from './router'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* OnePcBridge — unified cross-iframe communication SDK.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* // Leaf node (C - openclaw)
|
|
9
|
+
* const bridge = new OnePcBridge({
|
|
10
|
+
* nodeId: 'openclaw',
|
|
11
|
+
* allowedOrigins: ['https://...'],
|
|
12
|
+
* parentWindow: window.parent,
|
|
13
|
+
* })
|
|
14
|
+
* bridge.init()
|
|
15
|
+
* bridge.send('onepcweb', 'menu', { data: '...' })
|
|
16
|
+
*
|
|
17
|
+
* // Middle node (B - intelligent-web)
|
|
18
|
+
* const bridge = new OnePcBridge({
|
|
19
|
+
* nodeId: 'intelligent',
|
|
20
|
+
* allowedOrigins: ['https://...', CHILD_ORIGIN],
|
|
21
|
+
* parentWindow: window.parent,
|
|
22
|
+
* getChildWindow: () => iframeRef.value?.contentWindow,
|
|
23
|
+
* })
|
|
24
|
+
* bridge.addRoute('openclaw', 'onepcweb')
|
|
25
|
+
* bridge.init()
|
|
26
|
+
*/
|
|
27
|
+
export class OnePcBridge {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config
|
|
30
|
+
this.handlers = new Map()
|
|
31
|
+
this.router = new Router({
|
|
32
|
+
parentWindow: config.parentWindow,
|
|
33
|
+
getChildWindow: config.getChildWindow,
|
|
34
|
+
debug: config.debug ?? false,
|
|
35
|
+
})
|
|
36
|
+
this.messageHandler = null
|
|
37
|
+
this.initialized = false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
init() {
|
|
41
|
+
if (this.initialized) return
|
|
42
|
+
|
|
43
|
+
this.messageHandler = this.handleMessage.bind(this)
|
|
44
|
+
window.addEventListener('message', this.messageHandler)
|
|
45
|
+
this.initialized = true
|
|
46
|
+
this.log('initialized', this.config.nodeId)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
destroy() {
|
|
50
|
+
if (this.messageHandler) {
|
|
51
|
+
window.removeEventListener('message', this.messageHandler)
|
|
52
|
+
this.messageHandler = null
|
|
53
|
+
}
|
|
54
|
+
this.handlers.clear()
|
|
55
|
+
this.initialized = false
|
|
56
|
+
this.log('destroyed')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
send(target, type, payload) {
|
|
60
|
+
const envelope = createEnvelope(this.config.nodeId, target, type, payload)
|
|
61
|
+
this.log('send:', `${this.config.nodeId} → ${target}`, type, payload)
|
|
62
|
+
|
|
63
|
+
const targetWindow = this.getTargetWindow(target)
|
|
64
|
+
if (targetWindow) {
|
|
65
|
+
targetWindow.postMessage(serializeEnvelope(envelope), '*')
|
|
66
|
+
} else {
|
|
67
|
+
this.warn('no target window for:', target)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
on(type, handler) {
|
|
72
|
+
if (!this.handlers.has(type)) {
|
|
73
|
+
this.handlers.set(type, new Set())
|
|
74
|
+
}
|
|
75
|
+
this.handlers.get(type).add(handler)
|
|
76
|
+
|
|
77
|
+
return () => {
|
|
78
|
+
const set = this.handlers.get(type)
|
|
79
|
+
if (set) {
|
|
80
|
+
set.delete(handler)
|
|
81
|
+
if (set.size === 0) {
|
|
82
|
+
this.handlers.delete(type)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
addRoute(from, to) {
|
|
89
|
+
this.router?.addRoute(from, to)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
removeRoute(from, to) {
|
|
93
|
+
this.router?.removeRoute(from, to)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Private ────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
handleMessage(event) {
|
|
99
|
+
if (event.origin && !this.config.allowedOrigins.includes(event.origin)) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const envelope = parseEnvelope(event.data)
|
|
104
|
+
if (!envelope) return
|
|
105
|
+
|
|
106
|
+
if (envelope.from === this.config.nodeId && envelope.to === this.config.nodeId) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (envelope.to !== this.config.nodeId) {
|
|
111
|
+
if (this.router?.process(envelope)) {
|
|
112
|
+
this.dispatch(envelope)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.dispatch(envelope)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
dispatch(envelope) {
|
|
122
|
+
const typeHandlers = this.handlers.get(envelope.type)
|
|
123
|
+
if (typeHandlers) {
|
|
124
|
+
for (const handler of typeHandlers) {
|
|
125
|
+
try {
|
|
126
|
+
handler(envelope.payload, envelope)
|
|
127
|
+
} catch (err) {
|
|
128
|
+
this.warn('handler error:', envelope.type, err)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getTargetWindow(target) {
|
|
135
|
+
if (this.config.parentWindow && target !== this.config.nodeId) {
|
|
136
|
+
return this.config.parentWindow
|
|
137
|
+
}
|
|
138
|
+
if (this.config.getChildWindow) {
|
|
139
|
+
return this.config.getChildWindow()
|
|
140
|
+
}
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
log(...args) {
|
|
145
|
+
if (this.config.debug) {
|
|
146
|
+
console.log(`[OnePcBridge:${this.config.nodeId}]`, ...args)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
warn(...args) {
|
|
151
|
+
console.warn(`[OnePcBridge:${this.config.nodeId}]`, ...args)
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/envelope.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
let _counter = 0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a lightweight unique ID without external dependencies.
|
|
5
|
+
* Uses timestamp + counter for uniqueness within a session.
|
|
6
|
+
*/
|
|
7
|
+
function generateId() {
|
|
8
|
+
const ts = Date.now().toString(36)
|
|
9
|
+
const seq = (++_counter).toString(36)
|
|
10
|
+
return `mb_${ts}_${seq}`
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a new message envelope (new protocol format).
|
|
15
|
+
*/
|
|
16
|
+
export function createEnvelope(from, to, type, payload) {
|
|
17
|
+
return {
|
|
18
|
+
protocol: 'message-bridge-v1',
|
|
19
|
+
id: generateId(),
|
|
20
|
+
from,
|
|
21
|
+
to,
|
|
22
|
+
type,
|
|
23
|
+
payload: payload ?? null,
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse a raw postMessage event into an Envelope.
|
|
30
|
+
* Supports both the new protocol format and the legacy format
|
|
31
|
+
* for backward compatibility during migration.
|
|
32
|
+
*
|
|
33
|
+
* New format:
|
|
34
|
+
* { protocol: 'message-bridge-v1', from, to, type, payload, ... }
|
|
35
|
+
*
|
|
36
|
+
* Legacy format:
|
|
37
|
+
* { to, from, id, message: { subtype: 'webToPc', data: JSON.stringify({type, ...}) } }
|
|
38
|
+
*/
|
|
39
|
+
export function parseEnvelope(raw) {
|
|
40
|
+
if (!raw) return null
|
|
41
|
+
|
|
42
|
+
// --- New protocol ---
|
|
43
|
+
if (raw.protocol === 'message-bridge-v1') {
|
|
44
|
+
return raw
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// --- Legacy format detection ---
|
|
48
|
+
const legacy = raw
|
|
49
|
+
if (legacy.message && legacy.message.subtype === 'webToPc') {
|
|
50
|
+
const data = legacy.message.data
|
|
51
|
+
let parsed
|
|
52
|
+
|
|
53
|
+
if (typeof data === 'string') {
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(data)
|
|
56
|
+
} catch {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
} else if (typeof data === 'object') {
|
|
60
|
+
parsed = data
|
|
61
|
+
} else {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { type, ...restPayload } = parsed
|
|
66
|
+
if (!type) return null
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
protocol: 'message-bridge-v1',
|
|
70
|
+
id: legacy.id || generateId(),
|
|
71
|
+
from: detectFromNode(legacy.from),
|
|
72
|
+
to: detectToNode(legacy.to),
|
|
73
|
+
type: String(type),
|
|
74
|
+
payload: restPayload,
|
|
75
|
+
timestamp: Date.now(),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Legacy: openclawToPc subtype
|
|
80
|
+
if (legacy.message && legacy.message.subtype === 'openclawToPc') {
|
|
81
|
+
return {
|
|
82
|
+
protocol: 'message-bridge-v1',
|
|
83
|
+
id: legacy.id || generateId(),
|
|
84
|
+
from: detectFromNode(legacy.from),
|
|
85
|
+
to: 'onepcweb',
|
|
86
|
+
type: 'openclawToPc',
|
|
87
|
+
payload: legacy.message.data,
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Detect the source NodeId from legacy message's `from` field.
|
|
97
|
+
*/
|
|
98
|
+
function detectFromNode(from) {
|
|
99
|
+
if (!from) return 'intelligent'
|
|
100
|
+
if (from.includes('openclaw') || from.includes('claw')) return 'openclaw'
|
|
101
|
+
if (from.includes('intelligent')) return 'intelligent'
|
|
102
|
+
if (from.includes('vantage') || from.includes('onepc')) return 'onepcweb'
|
|
103
|
+
return 'intelligent'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Detect the target NodeId from legacy message's `to` field.
|
|
108
|
+
*/
|
|
109
|
+
function detectToNode(to) {
|
|
110
|
+
if (!to) return 'intelligent'
|
|
111
|
+
if (to.includes('pcWeb') || to.includes('pcMessage')) return 'onepcweb'
|
|
112
|
+
if (to.includes('intelligent')) return 'intelligent'
|
|
113
|
+
if (to.includes('openclaw') || to.includes('claw')) return 'openclaw'
|
|
114
|
+
return 'intelligent'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Serialize an Envelope for postMessage transmission.
|
|
119
|
+
*/
|
|
120
|
+
export function serializeEnvelope(envelope) {
|
|
121
|
+
return { ...envelope }
|
|
122
|
+
}
|
package/src/index.js
ADDED
package/src/router.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createEnvelope, serializeEnvelope } from './envelope'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Router manages automatic message forwarding for middle-layer nodes.
|
|
5
|
+
*/
|
|
6
|
+
export class Router {
|
|
7
|
+
constructor(opts = {}) {
|
|
8
|
+
this.routes = []
|
|
9
|
+
this.parentWindow = opts.parentWindow ?? null
|
|
10
|
+
this.getChildWindow = opts.getChildWindow ?? null
|
|
11
|
+
this.debug = opts.debug ?? false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
addRoute(from, to) {
|
|
15
|
+
this.removeRoute(from, to)
|
|
16
|
+
this.routes.push({ from, to })
|
|
17
|
+
this.log('route registered:', `${from} → ${to}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
removeRoute(from, to) {
|
|
21
|
+
this.routes = this.routes.filter(
|
|
22
|
+
r => !(r.from === from && r.to === to)
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
clearRoutes() {
|
|
27
|
+
this.routes = []
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getForwardTarget(envelope) {
|
|
31
|
+
const route = this.routes.find(
|
|
32
|
+
r => r.from === envelope.from && r.to === envelope.to
|
|
33
|
+
)
|
|
34
|
+
if (!route) return null
|
|
35
|
+
|
|
36
|
+
if (envelope.to === 'onepcweb' && this.parentWindow) {
|
|
37
|
+
return this.parentWindow
|
|
38
|
+
}
|
|
39
|
+
if (envelope.from === 'onepcweb' && this.getChildWindow) {
|
|
40
|
+
return this.getChildWindow()
|
|
41
|
+
}
|
|
42
|
+
if (envelope.to !== 'intelligent' && this.parentWindow) {
|
|
43
|
+
return this.parentWindow
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
forward(envelope) {
|
|
50
|
+
const target = this.getForwardTarget(envelope)
|
|
51
|
+
if (!target) return false
|
|
52
|
+
|
|
53
|
+
const body = serializeEnvelope(envelope)
|
|
54
|
+
target.postMessage(body, '*')
|
|
55
|
+
this.log('forwarded:', `${envelope.from} → ${envelope.to} via postMessage`, envelope.type)
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
hasRoute(from, to) {
|
|
60
|
+
return this.routes.some(r => r.from === from && r.to === to)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
process(envelope) {
|
|
64
|
+
if (this.hasRoute(envelope.from, envelope.to)) {
|
|
65
|
+
return this.forward(envelope)
|
|
66
|
+
}
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
log(...args) {
|
|
71
|
+
if (this.debug) {
|
|
72
|
+
console.log('[OnePcBridge-Router]', ...args)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type NodeId = 'onepcweb' | 'intelligent' | 'openclaw'
|
|
2
|
+
|
|
3
|
+
export interface Envelope {
|
|
4
|
+
protocol: 'message-bridge-v1'
|
|
5
|
+
id: string
|
|
6
|
+
from: NodeId
|
|
7
|
+
to: NodeId
|
|
8
|
+
type: string
|
|
9
|
+
payload: any
|
|
10
|
+
timestamp: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Config {
|
|
14
|
+
nodeId: NodeId
|
|
15
|
+
allowedOrigins: string[]
|
|
16
|
+
parentWindow?: Window
|
|
17
|
+
getChildWindow?: () => Window | null
|
|
18
|
+
debug?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Unsubscribe = () => void
|
|
22
|
+
|
|
23
|
+
export type MessageHandler = (payload: any, envelope: Envelope) => void
|
|
24
|
+
|
|
25
|
+
export class OnePcBridge {
|
|
26
|
+
constructor(config: Config)
|
|
27
|
+
init(): void
|
|
28
|
+
destroy(): void
|
|
29
|
+
send(target: NodeId, type: string, payload?: any): void
|
|
30
|
+
on(type: string, handler: MessageHandler): Unsubscribe
|
|
31
|
+
addRoute(from: NodeId, to: NodeId): void
|
|
32
|
+
removeRoute(from: NodeId, to: NodeId): void
|
|
33
|
+
}
|