@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 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": "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",
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
+ }
@@ -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
+ }
@@ -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
@@ -0,0 +1,3 @@
1
+ export { OnePcBridge } from './MessageBridge'
2
+ export { createEnvelope, parseEnvelope, serializeEnvelope } from './envelope'
3
+ export { Router } from './router'
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
+ }