iframe-bridge-kit 1.1.0 → 1.1.1

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 CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  [English](./README.md) | [简体中文](https://github.com/mchao123/iframe-bridge-kit/blob/master/README_zh.md)
4
4
 
5
- `iframe-bridge-kit` is an iframe communication library based on [Vite](https://vitejs.dev/) and [Penpal](https://www.google.com/search?q=https://github.com/google/penpal). It uses a Vite plugin to automatically generate type definitions, allowing the parent window and iframe child window to expose RPC methods to each other with **100% TypeScript type hints**, just like calling local functions.
5
+ `iframe-bridge-kit` is an iframe communication library based on [Vite](https://vitejs.dev/) and [Penpal](https://www.google.com/search?q=https://github.com/google/penpal). It uses a Vite plugin to automatically generate type definitions, allowing you to call parent window methods from the iframe (child window) with **100% TypeScript type hints**, just like calling local functions.
6
6
 
7
7
  ## ✨ Features
8
8
 
9
9
  * 🔒 **Type Safe**: Automatically generates `.d.ts` based on source code; parent and child windows share identical types.
10
- * 🚀 **No Duplicate Type Definitions**: The other window does not need to manually sync interfaces; simply import the generated bridge file to use.
10
+ * 🚀 **Zero Runtime Definition**: No need to manually define interfaces in the child window; simply import the generated bridge file to use.
11
11
  * 📡 **RPC Style**: Call cross-window methods just like calling `async` functions.
12
- * ⚡ **Event Mechanism**: Supports strongly-typed broadcast messages from both parent and iframe child windows.
12
+ * ⚡ **Event Mechanism**: Supports sending strongly-typed broadcast messages from the parent window to the child window.
13
13
  * 🛠 **Vite Integration**: Designed specifically for the Vite ecosystem with HMR support.
14
14
 
15
15
  ## 📦 Installation
@@ -49,7 +49,7 @@ export default defineConfig({
49
49
 
50
50
  ## 📖 Usage Guide
51
51
 
52
- ### 1\. Parent Defines APIs, Iframe Calls Parent
52
+ ### 1\. Parent Window (Host/Parent)
53
53
 
54
54
  In the parent window, use `defineBridge` to define the methods and event types exposed to the iframe.
55
55
 
@@ -100,80 +100,62 @@ In the iframe project, **directly import the file generated by the plugin**. All
100
100
  ```typescript
101
101
  // src/views/IframeChild.vue
102
102
  // Import from the generated directory (path depends on your outDir config)
103
- import ParentApi, { onMessage, onInit } from '../bridges/app-bridge'
103
+ import createParentBridge from '../bridges/app-bridge'
104
+
105
+ const parent = createParentBridge()
104
106
 
105
107
  // Wait for connection initialization (optional)
106
- onInit(() => {
108
+ parent.onInit(() => {
107
109
  console.log('Bridge connected!')
108
110
  })
109
111
 
110
112
  // 1. Call parent window methods (RPC)
111
113
  async function fetchUser() {
112
114
  // ✅ Full type hints for id and return value here!
113
- const user = await ParentApi.getUserInfo('123')
115
+ const user = await parent.getUserInfo('123')
114
116
  console.log(user.name)
115
117
  }
116
118
 
117
119
  // 2. Listen for parent window messages
118
120
  // ✅ Type hints for 'theme-change' and callback data
119
- onMessage('theme-change', (data) => {
121
+ parent.onMessage('theme-change', (data) => {
120
122
  console.log('New theme:', data.mode)
121
123
  })
122
124
  ```
123
125
 
124
- ### 3\. Iframe Defines APIs, Parent Calls Iframe
126
+ ### 3\. Reverse Calls: Child Defines API, Parent Calls It
125
127
 
126
- If your main use case is to build an iframe page first and let the parent window integrate with it, use `defineIframeBridge` inside the iframe project. The plugin will generate a parent-side client from the methods exposed by the iframe.
128
+ If the API is defined inside the iframe, the parent can import the same generated file and explicitly pass the target `Window` to establish the connection. Each default factory call returns an independent instance, so it works cleanly with multiple iframes.
127
129
 
128
130
  ```typescript
129
- // src/iframeBridge.ts (inside the iframe project)
130
- import { defineIframeBridge } from 'iframe-bridge-kit'
131
-
132
- interface ChildEmitMap {
133
- 'ready': { url: string }
134
- 'height-change': { height: number }
135
- }
136
-
137
- export const childBridge = defineIframeBridge<ChildEmitMap>('child-app', {
138
- async getPageInfo() {
139
- return {
140
- title: document.title,
141
- url: location.href
142
- }
143
- },
131
+ // inside iframe
132
+ import { defineBridge } from 'iframe-bridge-kit'
144
133
 
145
- setTheme(mode: 'dark' | 'light') {
146
- document.documentElement.dataset.theme = mode
147
- return true
134
+ export const childBridge = defineBridge('child-api', {
135
+ async getSelection() {
136
+ return window.getSelection()?.toString() || ''
148
137
  }
149
138
  })
150
139
 
151
- childBridge.connect().then((parent) => {
152
- parent.emit('ready', { url: location.href })
153
- })
140
+ childBridge.create(window.parent)
154
141
  ```
155
142
 
156
- After saving, the plugin generates `src/bridges/child-app/`. The parent project can import that directory and call `create(iframe)` to get strongly-typed methods exposed by the iframe.
157
-
158
143
  ```typescript
159
- // src/views/Parent.vue (inside the parent project)
160
- import ChildBridge from '../bridges/child-app'
144
+ // parent window
145
+ import { createBridgeClient } from '../bridges/child-api'
161
146
 
162
- const iframe = document.querySelector<HTMLIFrameElement>('#child-app')!
163
- const child = ChildBridge.create(iframe)
147
+ const iframeEl = document.querySelector('iframe')!
164
148
 
165
- child.onInit(async () => {
166
- const info = await child.api.getPageInfo()
167
- console.log(info.title)
168
-
169
- await child.api.setTheme('dark')
170
- })
149
+ if (!iframeEl.contentWindow) {
150
+ throw new Error('iframe is not ready')
151
+ }
171
152
 
172
- child.onMessage('height-change', ({ height }) => {
173
- iframe.style.height = `${height}px`
174
- })
153
+ const child = createBridgeClient(iframeEl.contentWindow)
154
+ const text = await child.getSelection()
175
155
  ```
176
156
 
157
+ The default child-window flow can call the default function, which reuses a singleton client connected to `window.parent`. When the parent wants to call APIs defined by the iframe, create an independent instance with `createBridgeClient(iframe.contentWindow)`.
158
+
177
159
  ## 🧩 Type Support Details
178
160
 
179
161
  The core magic of `iframe-bridge-kit` lies in how it handles types.
@@ -203,17 +185,7 @@ The plugin extracts the `User` interface (even types imported from `node_modules
203
185
 
204
186
  Returns an object containing:
205
187
 
206
- * `create(iframeEl, allowedOrigins?)`: Initializes the connection and returns an `{ emit }` object.
207
-
208
- ### `defineIframeBridge<TEmit>(name, methods)`
209
-
210
- * **name**: `string` - Bridge name, determines the generated directory name.
211
- * **methods**: `Object` - Collection of methods exposed from the iframe to the parent window.
212
- * **TEmit**: `Generic` - (Optional) Defines the event type mapping for messages sent via `emit` from the iframe.
213
-
214
- Returns an object containing:
215
-
216
- * `connect(allowedOrigins?)`: Connects to the parent window from inside the iframe and returns an `{ emit, destroy }` object.
188
+ * `create(target, allowedOrigins?)`: Initializes the connection. `target` may be an `HTMLIFrameElement` or `Window`, and the return value is an `{ emit, destroy }` object.
217
189
 
218
190
  ### Vite Plugin Options (`IframeBridgeOptions`)
219
191
 
@@ -224,33 +196,23 @@ Returns an object containing:
224
196
  | `full` | `boolean` | `true` | Whether to generate code containing full dependencies. |
225
197
  | `preserveModules` | `string[]` | `[]` | Preserve imports for specific modules instead of expanding types (e.g., `['vue']`). |
226
198
 
227
- ### Generated Child API
199
+ ### Generated Runtime API
228
200
 
229
201
  Assuming `outDir` is `src/bridges` and the bridge name is `my-bridge`, you can import from `src/bridges/my-bridge`:
230
202
 
231
- * **`default` (ParentApi)**: A proxy object containing all parent methods. All methods return a `Promise`.
232
- * **`onMessage(type, callback, once?)`**: Listen for events sent by the parent.
233
- * **`offMessage(type, callback?)`**: Remove an event listener.
234
- * **`onInit(callback)`**: Triggered when the connection is successfully established.
235
- * **`isInit()`**: Returns the current connection status.
236
-
237
- ### Generated Parent API
238
-
239
- When the bridge comes from `defineIframeBridge`, the same generated directory exports a parent-side client:
240
-
241
- * **`create(iframeEl, allowedOrigins?)`**: Initializes the connection to the iframe and returns a `BridgeConnection`.
242
- * **`connection.api`**: A strongly-typed proxy for methods exposed by the iframe. All methods return a `Promise`.
243
- * **`connection.onMessage(type, callback, once?)`**: Listen for events sent by the iframe.
244
- * **`connection.offMessage(type, callback?)`**: Remove an event listener.
245
- * **`connection.onInit(callback)`**: Triggered when the connection is successfully established.
246
- * **`connection.isInit()`**: Returns the current connection status.
247
- * **`connection.destroy()`**: Disconnects the current connection.
203
+ * **`default` (createBridge)**: Returns the default singleton `BridgeClient` connected to `window.parent`, suitable for child-to-parent calls.
204
+ * **`createBridgeClient(remoteWindow, allowedOrigins?)`**: Named multi-instance factory export. It requires an explicit `Window` and is suitable when the parent manages one or more iframes.
205
+ * **`client.onMessage(type, callback, once?)`**: Listen for events sent by the remote bridge.
206
+ * **`client.offMessage(type, callback?)`**: Remove an event listener.
207
+ * **`client.onInit(callback)`**: Triggered when that instance is successfully connected.
208
+ * **`client.isInit()`**: Returns that instance's connection status.
209
+ * **`client.destroy()`**: Destroy the connection maintained by that instance.
248
210
 
249
211
  ## ⚠️ Notes
250
212
 
251
213
  1. **Same-Origin Policy**: While Penpal simplifies postMessage, please ensure `allowedOrigins` is correctly configured for security.
252
214
  2. **Build Order**: During production builds, ensure files containing `defineBridge` are correctly processed. Usually, as long as these files are within your source tree (referenced via import), the Vite plugin will scan them.
253
- 3. **JSON Serialization**: Data transmitted across windows must be JSON serializable (Functions, DOM nodes, etc., are not supported).
215
+ 3. **Serialization**: Function values passed directly or nested in arrays/plain objects are proxied automatically, so callback-style APIs can cross the bridge. DOM nodes and other non-transferable objects are still not supported.
254
216
 
255
217
  ## License
256
218
 
package/dist/full/core.js CHANGED
@@ -1 +1 @@
1
- "use strict";var R=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(e,t)=>{for(var r in t)R(e,r,{get:t[r],enumerable:!0})},te=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Q(t))!Z.call(e,s)&&s!==r&&R(e,s,{get:()=>t[s],enumerable:!(n=B(t,s))||n.enumerable});return e};var re=e=>te(R({},"__esModule",{value:!0}),e);var Fe={};ee(Fe,{default:()=>De,isInit:()=>ke,offMessage:()=>D,onInit:()=>He,onMessage:()=>be});module.exports=re(Fe);var ne=class extends Error{code;constructor(e,t){super(t),this.name="PenpalError",this.code=e}},u=ne,se=e=>({name:e.name,message:e.message,stack:e.stack,penpalCode:e instanceof u?e.code:void 0}),ae=({name:e,message:t,stack:r,penpalCode:n})=>{let s=n?new u(n,t):new Error(t);return s.name=e,s.stack=r,s},oe=Symbol("Reply"),ie=class{value;transferables;#t=oe;constructor(e,t){this.value=e,this.transferables=t?.transferables}},de=ie,p="penpal",C=e=>typeof e=="object"&&e!==null,V=e=>typeof e=="function",le=e=>C(e)&&e.namespace===p,N=e=>e.type==="SYN",L=e=>e.type==="ACK1",T=e=>e.type==="ACK2",W=e=>e.type==="CALL",Y=e=>e.type==="REPLY",ce=e=>e.type==="DESTROY",j=(e,t=[])=>{let r=[];for(let n of Object.keys(e)){let s=e[n];V(s)?r.push([...t,n]):C(s)&&r.push(...j(s,[...t,n]))}return r},he=(e,t)=>{let r=e.reduce((n,s)=>C(n)?n[s]:void 0,t);return V(r)?r:void 0},g=e=>e.join("."),F=(e,t,r)=>({namespace:p,channel:e,type:"REPLY",callId:t,isError:!0,...r instanceof Error?{value:se(r),isSerializedErrorInstance:!0}:{value:r}}),ue=(e,t,r,n)=>{let s=!1,l=async f=>{if(s||!W(f))return;n?.(`Received ${g(f.methodPath)}() call`,f);let{methodPath:v,args:c,id:o}=f,a,M;try{let d=he(v,t);if(!d)throw new u("METHOD_NOT_FOUND",`Method \`${g(v)}\` is not found.`);let h=await d(...c);h instanceof de&&(M=h.transferables,h=await h.value),a={namespace:p,channel:r,type:"REPLY",callId:o,value:h}}catch(d){a=F(r,o,d)}if(!s)try{n?.(`Sending ${g(v)}() reply`,a),e.sendMessage(a,M)}catch(d){throw d.name==="DataCloneError"&&(a=F(r,o,d),n?.(`Sending ${g(v)}() reply`,a),e.sendMessage(a)),d}};return e.addMessageHandler(l),()=>{s=!0,e.removeMessageHandler(l)}},fe=ue,z=crypto.randomUUID?.bind(crypto)??(()=>new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")),pe=Symbol("CallOptions"),ve=class{transferables;timeout;#t=pe;constructor(e){this.transferables=e?.transferables,this.timeout=e?.timeout}},Me=ve,ge=new Set(["apply","call","bind"]),K=(e,t,r=[])=>new Proxy(r.length?()=>{}:Object.create(null),{get(n,s){if(s!=="then")return r.length&&ge.has(s)?Reflect.get(n,s):K(e,t,[...r,s])},apply(n,s,l){return e(r,l)}}),x=e=>new u("CONNECTION_DESTROYED",`Method call ${g(e)}() failed due to destroyed connection`),ye=(e,t,r)=>{let n=!1,s=new Map,l=c=>{if(!Y(c))return;let{callId:o,value:a,isError:M,isSerializedErrorInstance:d}=c,h=s.get(o);h&&(s.delete(o),r?.(`Received ${g(h.methodPath)}() call`,c),M?h.reject(d?ae(a):a):h.resolve(a))};return e.addMessageHandler(l),{remoteProxy:K((c,o)=>{if(n)throw x(c);let a=z(),M=o[o.length-1],d=M instanceof Me,{timeout:h,transferables:_}=d?M:{},A=d?o.slice(0,-1):o;return new Promise((S,I)=>{let P=h!==void 0?window.setTimeout(()=>{s.delete(a),I(new u("METHOD_CALL_TIMEOUT",`Method call ${g(c)}() timed out after ${h}ms`))},h):void 0;s.set(a,{methodPath:c,resolve:S,reject:I,timeoutId:P});try{let E={namespace:p,channel:t,type:"CALL",id:a,methodPath:c,args:A};r?.(`Sending ${g(c)}() call`,E),e.sendMessage(E,_)}catch(E){I(new u("TRANSMISSION_FAILED",E.message))}})},r),destroy:()=>{n=!0,e.removeMessageHandler(l);for(let{methodPath:c,reject:o,timeoutId:a}of s.values())clearTimeout(a),o(x(c));s.clear()}}},Ee=ye,Ie=()=>{let e,t;return{promise:new Promise((n,s)=>{e=n,t=s}),resolve:e,reject:t}},we=Ie,O="deprecated-penpal",Ae=e=>C(e)&&"penpal"in e,Se=e=>e.split("."),U=e=>e.join("."),me=e=>{try{return JSON.stringify(e)}catch{return String(e)}},G=e=>new u("TRANSMISSION_FAILED",`Unexpected message to translate: ${me(e)}`),Ne=e=>{if(e.penpal==="syn")return{namespace:p,channel:void 0,type:"SYN",participantId:O};if(e.penpal==="ack")return{namespace:p,channel:void 0,type:"ACK2"};if(e.penpal==="call")return{namespace:p,channel:void 0,type:"CALL",id:e.id,methodPath:Se(e.methodName),args:e.args};if(e.penpal==="reply")return e.resolution==="fulfilled"?{namespace:p,channel:void 0,type:"REPLY",callId:e.id,value:e.returnValue}:{namespace:p,channel:void 0,type:"REPLY",callId:e.id,isError:!0,...e.returnValueIsError?{value:e.returnValue,isSerializedErrorInstance:!0}:{value:e.returnValue}};throw G(e)},Ce=e=>{if(L(e))return{penpal:"synAck",methodNames:e.methodPaths.map(U)};if(W(e))return{penpal:"call",id:e.id,methodName:U(e.methodPath),args:e.args};if(Y(e))return e.isError?{penpal:"reply",id:e.callId,resolution:"rejected",...e.isSerializedErrorInstance?{returnValue:e.value,returnValueIsError:!0}:{returnValue:e.value}}:{penpal:"reply",id:e.callId,resolution:"fulfilled",returnValue:e.value};throw G(e)},_e=({messenger:e,methods:t,timeout:r,channel:n,log:s})=>{let l=z(),f,v=[],c=!1,o=j(t),{promise:a,resolve:M,reject:d}=we(),h=r!==void 0?setTimeout(()=>{d(new u("CONNECTION_TIMEOUT",`Connection timed out after ${r}ms`))},r):void 0,_=()=>{for(let i of v)i()},A=()=>{if(c)return;v.push(fe(e,t,n,s));let{remoteProxy:i,destroy:y}=Ee(e,n,s);v.push(y),clearTimeout(h),c=!0,M({remoteProxy:i,destroy:_})},S=()=>{let i={namespace:p,type:"SYN",channel:n,participantId:l};s?.("Sending handshake SYN",i);try{e.sendMessage(i)}catch(y){d(new u("TRANSMISSION_FAILED",y.message))}},I=i=>{if(s?.("Received handshake SYN",i),i.participantId===f&&f!==O||(f=i.participantId,S(),!(l>f||f===O)))return;let m={namespace:p,channel:n,type:"ACK1",methodPaths:o};s?.("Sending handshake ACK1",m);try{e.sendMessage(m)}catch(q){d(new u("TRANSMISSION_FAILED",q.message));return}},P=i=>{s?.("Received handshake ACK1",i);let y={namespace:p,channel:n,type:"ACK2"};s?.("Sending handshake ACK2",y);try{e.sendMessage(y)}catch(m){d(new u("TRANSMISSION_FAILED",m.message));return}A()},E=i=>{s?.("Received handshake ACK2",i),A()},H=i=>{N(i)&&I(i),L(i)&&P(i),T(i)&&E(i)};return e.addMessageHandler(H),v.push(()=>e.removeMessageHandler(H)),S(),a},Pe=_e,Re=e=>{let t=!1,r;return(...n)=>(t||(t=!0,r=e(...n)),r)},Te=Re,$=new WeakSet,Oe=({messenger:e,methods:t={},timeout:r,channel:n,log:s})=>{if(!e)throw new u("INVALID_ARGUMENT","messenger must be defined");if($.has(e))throw new u("INVALID_ARGUMENT","A messenger can only be used for a single connection");$.add(e);let l=[e.destroy],f=Te(o=>{if(o){let a={namespace:p,channel:n,type:"DESTROY"};try{e.sendMessage(a)}catch{}}for(let a of l)a();s?.("Connection destroyed")}),v=o=>le(o)&&o.channel===n;return{promise:(async()=>{try{e.initialize({log:s,validateReceivedMessage:v}),e.addMessageHandler(M=>{ce(M)&&f(!1)});let{remoteProxy:o,destroy:a}=await Pe({messenger:e,methods:t,timeout:r,channel:n,log:s});return l.push(a),o}catch(o){throw f(!0),o}})(),destroy:()=>{f(!0)}}},J=Oe,Le=class{#t;#s;#r;#i;#a;#n=new Set;#e;#o=!1;constructor({remoteWindow:e,allowedOrigins:t}){if(!e)throw new u("INVALID_ARGUMENT","remoteWindow must be defined");this.#t=e,this.#s=t?.length?t:[window.origin]}initialize=({log:e,validateReceivedMessage:t})=>{this.#r=e,this.#i=t,window.addEventListener("message",this.#h)};sendMessage=(e,t)=>{if(N(e)){let r=this.#d(e);this.#t.postMessage(e,{targetOrigin:r,transfer:t});return}if(L(e)||this.#o){let r=this.#o?Ce(e):e,n=this.#d(e);this.#t.postMessage(r,{targetOrigin:n,transfer:t});return}if(T(e)){let{port1:r,port2:n}=new MessageChannel;this.#e=r,r.addEventListener("message",this.#l),r.start();let s=[n,...t||[]],l=this.#d(e);this.#t.postMessage(e,{targetOrigin:l,transfer:s});return}if(this.#e){this.#e.postMessage(e,{transfer:t});return}throw new u("TRANSMISSION_FAILED","Cannot send message because the MessagePort is not connected")};addMessageHandler=e=>{this.#n.add(e)};removeMessageHandler=e=>{this.#n.delete(e)};destroy=()=>{window.removeEventListener("message",this.#h),this.#c(),this.#n.clear()};#u=e=>this.#s.some(t=>t instanceof RegExp?t.test(e):t===e||t==="*");#d=e=>{if(N(e))return"*";if(!this.#a)throw new u("TRANSMISSION_FAILED","Cannot send message because the remote origin is not established");return this.#a==="null"&&this.#s.includes("*")?"*":this.#a};#c=()=>{this.#e?.removeEventListener("message",this.#l),this.#e?.close(),this.#e=void 0};#h=({source:e,origin:t,ports:r,data:n})=>{if(e===this.#t){if(Ae(n)){this.#r?.("Please upgrade the child window to the latest version of Penpal."),this.#o=!0;try{n=Ne(n)}catch(s){this.#r?.(`Failed to translate deprecated message: ${s.message}`);return}}if(this.#i?.(n)){if(!this.#u(t)){this.#r?.(`Received a message from origin \`${t}\` which did not match allowed origins \`[${this.#s.join(", ")}]\``);return}if(N(n)&&(this.#c(),this.#a=t),T(n)&&!this.#o){if(this.#e=r[0],!this.#e){this.#r?.("Ignoring ACK2 because it did not include a MessagePort");return}this.#e.addEventListener("message",this.#l),this.#e.start()}for(let s of this.#n)s(n)}}};#l=({data:e})=>{if(this.#i?.(e))for(let t of this.#n)t(e)}},X=Le;var w=new Map,b=J({messenger:new X({remoteWindow:window.parent,allowedOrigins:["__AllowedOrigins__"]}),channel:"iframe-bridge-channel",methods:{onMessage(e,t){let r=w.get(e);if(r)return r.forEach(n=>n(t)),!0}}}),De=new Proxy({},{get(e,t){return async(...r)=>await b.promise.then(n=>n[t](...r))}}),be=(e,t,r)=>{let n=r?(l=>{t(l),D(e,n)}):t,s=w.get(e);return s?s.add(n):w.set(e,new Set([n])),()=>D(e,n)},D=(e,t)=>{if(!t){w.delete(e);return}let r=w.get(e);r&&r.delete(t)},k=!1;b.promise.then(()=>{k=!0});var ke=()=>k,He=e=>{if(k){e();return}b.promise.then(()=>{e()})};0&&(module.exports={isInit,offMessage,onInit,onMessage});
1
+ "use strict";var P=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var ne=Object.getOwnPropertyNames;var se=Object.prototype.hasOwnProperty;var ie=(e,t)=>{for(var r in t)P(e,r,{get:t[r],enumerable:!0})},oe=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of ne(t))!se.call(e,s)&&s!==r&&P(e,s,{get:()=>t[s],enumerable:!(n=re(t,s))||n.enumerable});return e};var ae=e=>oe(P({},"__esModule",{value:!0}),e);var $e={};ie($e,{createBridgeClient:()=>ee,default:()=>We});module.exports=ae($e);var ce=class extends Error{code;constructor(e,t){super(t),this.name="PenpalError",this.code=e}},y=ce,de=e=>({name:e.name,message:e.message,stack:e.stack,penpalCode:e instanceof y?e.code:void 0}),le=({name:e,message:t,stack:r,penpalCode:n})=>{let s=n?new y(n,t):new Error(t);return s.name=e,s.stack=r,s},he=Symbol("Reply"),ue=class{value;transferables;#t=he;constructor(e,t){this.value=e,this.transferables=t?.transferables}},fe=ue,g="penpal",R=e=>typeof e=="object"&&e!==null,$=e=>typeof e=="function",pe=e=>R(e)&&e.namespace===g,C=e=>e.type==="SYN",N=e=>e.type==="ACK1",S=e=>e.type==="ACK2",U=e=>e.type==="CALL",z=e=>e.type==="REPLY",ye=e=>e.type==="DESTROY",V=(e,t=[])=>{let r=[];for(let n of Object.keys(e)){let s=e[n];$(s)?r.push([...t,n]):R(s)&&r.push(...V(s,[...t,n]))}return r},ge=(e,t)=>{let r=e.reduce((n,s)=>R(n)?n[s]:void 0,t);return $(r)?r:void 0},m=e=>e.join("."),H=(e,t,r)=>({namespace:g,channel:e,type:"REPLY",callId:t,isError:!0,...r instanceof Error?{value:de(r),isSerializedErrorInstance:!0}:{value:r}}),Me=(e,t,r,n)=>{let s=!1,h=async d=>{if(s||!U(d))return;n?.(`Received ${m(d.methodPath)}() call`,d);let{methodPath:f,args:p,id:l}=d,i,a;try{let c=ge(f,t);if(!c)throw new y("METHOD_NOT_FOUND",`Method \`${m(f)}\` is not found.`);let o=await c(...p);o instanceof fe&&(a=o.transferables,o=await o.value),i={namespace:g,channel:r,type:"REPLY",callId:l,value:o}}catch(c){i=H(r,l,c)}if(!s)try{n?.(`Sending ${m(f)}() reply`,i),e.sendMessage(i,a)}catch(c){throw c.name==="DataCloneError"&&(i=H(r,l,c),n?.(`Sending ${m(f)}() reply`,i),e.sendMessage(i)),c}};return e.addMessageHandler(h),()=>{s=!0,e.removeMessageHandler(h)}},ve=Me,Y=crypto.randomUUID?.bind(crypto)??(()=>new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")),me=Symbol("CallOptions"),we=class{transferables;timeout;#t=me;constructor(e){this.transferables=e?.transferables,this.timeout=e?.timeout}},be=we,Ie=new Set(["apply","call","bind"]),B=(e,t,r=[])=>new Proxy(r.length?()=>{}:Object.create(null),{get(n,s){if(s!=="then")return r.length&&Ie.has(s)?Reflect.get(n,s):B(e,t,[...r,s])},apply(n,s,h){return e(r,h)}}),x=e=>new y("CONNECTION_DESTROYED",`Method call ${m(e)}() failed due to destroyed connection`),Ee=(e,t,r)=>{let n=!1,s=new Map,h=p=>{if(!z(p))return;let{callId:l,value:i,isError:a,isSerializedErrorInstance:c}=p,o=s.get(l);o&&(s.delete(l),r?.(`Received ${m(o.methodPath)}() call`,p),a?o.reject(c?le(i):i):o.resolve(i))};return e.addMessageHandler(h),{remoteProxy:B((p,l)=>{if(n)throw x(p);let i=Y(),a=l[l.length-1],c=a instanceof be,{timeout:o,transferables:M}=c?a:{},v=c?l.slice(0,-1):l;return new Promise((b,E)=>{let A=o!==void 0?window.setTimeout(()=>{s.delete(i),E(new y("METHOD_CALL_TIMEOUT",`Method call ${m(p)}() timed out after ${o}ms`))},o):void 0;s.set(i,{methodPath:p,resolve:b,reject:E,timeoutId:A});try{let I={namespace:g,channel:t,type:"CALL",id:i,methodPath:p,args:v};r?.(`Sending ${m(p)}() call`,I),e.sendMessage(I,M)}catch(I){E(new y("TRANSMISSION_FAILED",I.message))}})},r),destroy:()=>{n=!0,e.removeMessageHandler(h);for(let{methodPath:p,reject:l,timeoutId:i}of s.values())clearTimeout(i),l(x(p));s.clear()}}},ke=Ee,Ce=()=>{let e,t;return{promise:new Promise((n,s)=>{e=n,t=s}),resolve:e,reject:t}},Re=Ce,_="deprecated-penpal",Ae=e=>R(e)&&"penpal"in e,Pe=e=>e.split("."),j=e=>e.join("."),Se=e=>{try{return JSON.stringify(e)}catch{return String(e)}},K=e=>new y("TRANSMISSION_FAILED",`Unexpected message to translate: ${Se(e)}`),_e=e=>{if(e.penpal==="syn")return{namespace:g,channel:void 0,type:"SYN",participantId:_};if(e.penpal==="ack")return{namespace:g,channel:void 0,type:"ACK2"};if(e.penpal==="call")return{namespace:g,channel:void 0,type:"CALL",id:e.id,methodPath:Pe(e.methodName),args:e.args};if(e.penpal==="reply")return e.resolution==="fulfilled"?{namespace:g,channel:void 0,type:"REPLY",callId:e.id,value:e.returnValue}:{namespace:g,channel:void 0,type:"REPLY",callId:e.id,isError:!0,...e.returnValueIsError?{value:e.returnValue,isSerializedErrorInstance:!0}:{value:e.returnValue}};throw K(e)},Ne=e=>{if(N(e))return{penpal:"synAck",methodNames:e.methodPaths.map(j)};if(U(e))return{penpal:"call",id:e.id,methodName:j(e.methodPath),args:e.args};if(z(e))return e.isError?{penpal:"reply",id:e.callId,resolution:"rejected",...e.isSerializedErrorInstance?{returnValue:e.value,returnValueIsError:!0}:{returnValue:e.value}}:{penpal:"reply",id:e.callId,resolution:"fulfilled",returnValue:e.value};throw K(e)},Te=({messenger:e,methods:t,timeout:r,channel:n,log:s})=>{let h=Y(),d,f=[],p=!1,l=V(t),{promise:i,resolve:a,reject:c}=Re(),o=r!==void 0?setTimeout(()=>{c(new y("CONNECTION_TIMEOUT",`Connection timed out after ${r}ms`))},r):void 0,M=()=>{for(let u of f)u()},v=()=>{if(p)return;f.push(ve(e,t,n,s));let{remoteProxy:u,destroy:w}=ke(e,n,s);f.push(w),clearTimeout(o),p=!0,a({remoteProxy:u,destroy:M})},b=()=>{let u={namespace:g,type:"SYN",channel:n,participantId:h};s?.("Sending handshake SYN",u);try{e.sendMessage(u)}catch(w){c(new y("TRANSMISSION_FAILED",w.message))}},E=u=>{if(s?.("Received handshake SYN",u),u.participantId===d&&d!==_||(d=u.participantId,b(),!(h>d||d===_)))return;let k={namespace:g,channel:n,type:"ACK1",methodPaths:l};s?.("Sending handshake ACK1",k);try{e.sendMessage(k)}catch(te){c(new y("TRANSMISSION_FAILED",te.message));return}},A=u=>{s?.("Received handshake ACK1",u);let w={namespace:g,channel:n,type:"ACK2"};s?.("Sending handshake ACK2",w);try{e.sendMessage(w)}catch(k){c(new y("TRANSMISSION_FAILED",k.message));return}v()},I=u=>{s?.("Received handshake ACK2",u),v()},F=u=>{C(u)&&E(u),N(u)&&A(u),S(u)&&I(u)};return e.addMessageHandler(F),f.push(()=>e.removeMessageHandler(F)),b(),i},Oe=Te,Le=e=>{let t=!1,r;return(...n)=>(t||(t=!0,r=e(...n)),r)},De=Le,W=new WeakSet,Fe=({messenger:e,methods:t={},timeout:r,channel:n,log:s})=>{if(!e)throw new y("INVALID_ARGUMENT","messenger must be defined");if(W.has(e))throw new y("INVALID_ARGUMENT","A messenger can only be used for a single connection");W.add(e);let h=[e.destroy],d=De(l=>{if(l){let i={namespace:g,channel:n,type:"DESTROY"};try{e.sendMessage(i)}catch{}}for(let i of h)i();s?.("Connection destroyed")}),f=l=>pe(l)&&l.channel===n;return{promise:(async()=>{try{e.initialize({log:s,validateReceivedMessage:f}),e.addMessageHandler(a=>{ye(a)&&d(!1)});let{remoteProxy:l,destroy:i}=await Oe({messenger:e,methods:t,timeout:r,channel:n,log:s});return h.push(i),l}catch(l){throw d(!0),l}})(),destroy:()=>{d(!0)}}},G=Fe,He=class{#t;#s;#r;#a;#i;#n=new Set;#e;#o=!1;constructor({remoteWindow:e,allowedOrigins:t}){if(!e)throw new y("INVALID_ARGUMENT","remoteWindow must be defined");this.#t=e,this.#s=t?.length?t:[window.origin]}initialize=({log:e,validateReceivedMessage:t})=>{this.#r=e,this.#a=t,window.addEventListener("message",this.#h)};sendMessage=(e,t)=>{if(C(e)){let r=this.#c(e);this.#t.postMessage(e,{targetOrigin:r,transfer:t});return}if(N(e)||this.#o){let r=this.#o?Ne(e):e,n=this.#c(e);this.#t.postMessage(r,{targetOrigin:n,transfer:t});return}if(S(e)){let{port1:r,port2:n}=new MessageChannel;this.#e=r,r.addEventListener("message",this.#d),r.start();let s=[n,...t||[]],h=this.#c(e);this.#t.postMessage(e,{targetOrigin:h,transfer:s});return}if(this.#e){this.#e.postMessage(e,{transfer:t});return}throw new y("TRANSMISSION_FAILED","Cannot send message because the MessagePort is not connected")};addMessageHandler=e=>{this.#n.add(e)};removeMessageHandler=e=>{this.#n.delete(e)};destroy=()=>{window.removeEventListener("message",this.#h),this.#l(),this.#n.clear()};#u=e=>this.#s.some(t=>t instanceof RegExp?t.test(e):t===e||t==="*");#c=e=>{if(C(e))return"*";if(!this.#i)throw new y("TRANSMISSION_FAILED","Cannot send message because the remote origin is not established");return this.#i==="null"&&this.#s.includes("*")?"*":this.#i};#l=()=>{this.#e?.removeEventListener("message",this.#d),this.#e?.close(),this.#e=void 0};#h=({source:e,origin:t,ports:r,data:n})=>{if(e===this.#t){if(Ae(n)){this.#r?.("Please upgrade the child window to the latest version of Penpal."),this.#o=!0;try{n=_e(n)}catch(s){this.#r?.(`Failed to translate deprecated message: ${s.message}`);return}}if(this.#a?.(n)){if(!this.#u(t)){this.#r?.(`Received a message from origin \`${t}\` which did not match allowed origins \`[${this.#s.join(", ")}]\``);return}if(C(n)&&(this.#l(),this.#i=t),S(n)&&!this.#o){if(this.#e=r[0],!this.#e){this.#r?.("Ignoring ACK2 because it did not include a MessagePort");return}this.#e.addEventListener("message",this.#d),this.#e.start()}for(let s of this.#n)s(n)}}};#d=({data:e})=>{if(this.#a?.(e))for(let t of this.#n)t(e)}},J=He;var q="iframe-bridge-kit.callback",O="__iframeBridgeInvokeCallback",T=e=>typeof e=="object"&&e!==null,X=e=>{let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},xe=e=>T(e)&&e.__iframeBridgeType===q&&typeof e.id=="string",Q=e=>{let t=new Map,r=new WeakMap,n=new Map,s=0,h=i=>{let a=r.get(i);return a||(a=`${Date.now().toString(36)}-${(++s).toString(36)}`,r.set(i,a)),t.set(a,i),{__iframeBridgeType:q,id:a}},d=(i,a=new WeakMap)=>{if(typeof i=="function")return h(i);if(!T(i))return i;if(a.has(i))return a.get(i);if(Array.isArray(i)){let o=[];return a.set(i,o),i.forEach((M,v)=>{o[v]=d(M,a)}),o}if(!X(i))return i;let c={};return a.set(i,c),Object.keys(i).forEach(o=>{c[o]=d(i[o],a)}),c},f=(i,a=new WeakMap)=>{if(!T(i))return i;if(xe(i)){let o=n.get(i.id);return o||(o=async(...M)=>{let b=await(await e())[O](i.id,d(M));return f(b)},n.set(i.id,o)),o}if(a.has(i))return a.get(i);if(Array.isArray(i)){let o=[];return a.set(i,o),i.forEach((M,v)=>{o[v]=f(M,a)}),o}if(!X(i))return i;let c={};return a.set(i,c),Object.keys(i).forEach(o=>{c[o]=f(i[o],a)}),c};return{serialize:d,deserialize:f,invokeLocalCallback:async(i,a)=>{let c=t.get(i);if(!c)throw new Error(`Callback "${i}" is not available`);let o=await c(...f(a));return d(o)},clearCallbacks:()=>{t.clear(),n.clear()}}};var je="iframe-bridge-channel",Z=["__AllowedOrigins__"],D=class{msgProxy=new Map;initCallbacks=new Set;conn;remotePromise;inited=!1;connectionId=0;callbacks=Q(()=>this.getRemote());api;constructor(){let t={onMessage:this.onMessage,offMessage:this.offMessage,isInit:this.isInit,onInit:this.onInit,destroy:this.destroy};this.api=new Proxy({},{get:(r,n)=>{if(n!=="then"&&typeof n=="string")return n in t?t[n]:async(...s)=>{let d=(await this.getRemote())[n];if(typeof d!="function")throw new Error(`Remote method "${String(n)}" is not available`);return this.callbacks.deserialize(await d(...this.callbacks.serialize(s)))}}})}connect=(t,r=Z)=>{this.conn?.destroy(),this.inited=!1;let n=++this.connectionId;return this.conn=G({messenger:new J({remoteWindow:t,allowedOrigins:r}),channel:je,methods:{onMessage:this.receiveMessage,[O]:this.callbacks.invokeLocalCallback}}),this.remotePromise=this.conn.promise.then(s=>(n===this.connectionId&&(this.inited=!0,this.notifyInit()),s)),this.remotePromise};getRemote=()=>{if(!this.remotePromise){if(typeof window>"u"||window.parent===window)throw new Error("remoteWindow is not configured. Pass a Window when creating the bridge client.");return this.connect(window.parent)}return this.remotePromise};receiveMessage=(t,r)=>{let n=this.msgProxy.get(t);if(n){let s=this.callbacks.deserialize(r);return n.forEach(h=>h(s)),!0}};notifyInit=()=>{let t=Array.from(this.initCallbacks);this.initCallbacks.clear(),t.forEach(r=>r(this.api))};onMessage=(t,r,n)=>{let s=n?(d=>{r(d),this.offMessage(t,s)}):r,h=this.msgProxy.get(t);return h?h.add(s):this.msgProxy.set(t,new Set([s])),()=>this.offMessage(t,s)};offMessage=(t,r)=>{if(!r){this.msgProxy.delete(t);return}let n=this.msgProxy.get(t);n&&n.delete(r)};isInit=()=>this.inited;onInit=t=>{if(this.inited){t(this.api);return}this.initCallbacks.add(t)};destroy=()=>{this.connectionId++,this.conn?.destroy(),this.conn=void 0,this.remotePromise=void 0,this.inited=!1,this.initCallbacks.clear(),this.callbacks.clearCallbacks()}},ee=(e,t=Z)=>{let r=new D;return r.connect(e,t),r.api},L,We=()=>(L||(L=ee(window.parent)),L);0&&(module.exports={createBridgeClient});
@@ -1 +1 @@
1
- var q=class extends Error{code;constructor(e,t){super(t),this.name="PenpalError",this.code=e}},u=q,B=e=>({name:e.name,message:e.message,stack:e.stack,penpalCode:e instanceof u?e.code:void 0}),Q=({name:e,message:t,stack:r,penpalCode:n})=>{let s=n?new u(n,t):new Error(t);return s.name=e,s.stack=r,s},Z=Symbol("Reply"),ee=class{value;transferables;#t=Z;constructor(e,t){this.value=e,this.transferables=t?.transferables}},te=ee,p="penpal",C=e=>typeof e=="object"&&e!==null,U=e=>typeof e=="function",re=e=>C(e)&&e.namespace===p,N=e=>e.type==="SYN",O=e=>e.type==="ACK1",R=e=>e.type==="ACK2",$=e=>e.type==="CALL",V=e=>e.type==="REPLY",ne=e=>e.type==="DESTROY",W=(e,t=[])=>{let r=[];for(let n of Object.keys(e)){let s=e[n];U(s)?r.push([...t,n]):C(s)&&r.push(...W(s,[...t,n]))}return r},se=(e,t)=>{let r=e.reduce((n,s)=>C(n)?n[s]:void 0,t);return U(r)?r:void 0},g=e=>e.join("."),k=(e,t,r)=>({namespace:p,channel:e,type:"REPLY",callId:t,isError:!0,...r instanceof Error?{value:B(r),isSerializedErrorInstance:!0}:{value:r}}),ae=(e,t,r,n)=>{let s=!1,l=async f=>{if(s||!$(f))return;n?.(`Received ${g(f.methodPath)}() call`,f);let{methodPath:v,args:c,id:o}=f,a,M;try{let d=se(v,t);if(!d)throw new u("METHOD_NOT_FOUND",`Method \`${g(v)}\` is not found.`);let h=await d(...c);h instanceof te&&(M=h.transferables,h=await h.value),a={namespace:p,channel:r,type:"REPLY",callId:o,value:h}}catch(d){a=k(r,o,d)}if(!s)try{n?.(`Sending ${g(v)}() reply`,a),e.sendMessage(a,M)}catch(d){throw d.name==="DataCloneError"&&(a=k(r,o,d),n?.(`Sending ${g(v)}() reply`,a),e.sendMessage(a)),d}};return e.addMessageHandler(l),()=>{s=!0,e.removeMessageHandler(l)}},oe=ae,Y=crypto.randomUUID?.bind(crypto)??(()=>new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")),ie=Symbol("CallOptions"),de=class{transferables;timeout;#t=ie;constructor(e){this.transferables=e?.transferables,this.timeout=e?.timeout}},le=de,ce=new Set(["apply","call","bind"]),j=(e,t,r=[])=>new Proxy(r.length?()=>{}:Object.create(null),{get(n,s){if(s!=="then")return r.length&&ce.has(s)?Reflect.get(n,s):j(e,t,[...r,s])},apply(n,s,l){return e(r,l)}}),H=e=>new u("CONNECTION_DESTROYED",`Method call ${g(e)}() failed due to destroyed connection`),he=(e,t,r)=>{let n=!1,s=new Map,l=c=>{if(!V(c))return;let{callId:o,value:a,isError:M,isSerializedErrorInstance:d}=c,h=s.get(o);h&&(s.delete(o),r?.(`Received ${g(h.methodPath)}() call`,c),M?h.reject(d?Q(a):a):h.resolve(a))};return e.addMessageHandler(l),{remoteProxy:j((c,o)=>{if(n)throw H(c);let a=Y(),M=o[o.length-1],d=M instanceof le,{timeout:h,transferables:_}=d?M:{},A=d?o.slice(0,-1):o;return new Promise((S,I)=>{let P=h!==void 0?window.setTimeout(()=>{s.delete(a),I(new u("METHOD_CALL_TIMEOUT",`Method call ${g(c)}() timed out after ${h}ms`))},h):void 0;s.set(a,{methodPath:c,resolve:S,reject:I,timeoutId:P});try{let E={namespace:p,channel:t,type:"CALL",id:a,methodPath:c,args:A};r?.(`Sending ${g(c)}() call`,E),e.sendMessage(E,_)}catch(E){I(new u("TRANSMISSION_FAILED",E.message))}})},r),destroy:()=>{n=!0,e.removeMessageHandler(l);for(let{methodPath:c,reject:o,timeoutId:a}of s.values())clearTimeout(a),o(H(c));s.clear()}}},ue=he,fe=()=>{let e,t;return{promise:new Promise((n,s)=>{e=n,t=s}),resolve:e,reject:t}},pe=fe,T="deprecated-penpal",ve=e=>C(e)&&"penpal"in e,Me=e=>e.split("."),F=e=>e.join("."),ge=e=>{try{return JSON.stringify(e)}catch{return String(e)}},z=e=>new u("TRANSMISSION_FAILED",`Unexpected message to translate: ${ge(e)}`),ye=e=>{if(e.penpal==="syn")return{namespace:p,channel:void 0,type:"SYN",participantId:T};if(e.penpal==="ack")return{namespace:p,channel:void 0,type:"ACK2"};if(e.penpal==="call")return{namespace:p,channel:void 0,type:"CALL",id:e.id,methodPath:Me(e.methodName),args:e.args};if(e.penpal==="reply")return e.resolution==="fulfilled"?{namespace:p,channel:void 0,type:"REPLY",callId:e.id,value:e.returnValue}:{namespace:p,channel:void 0,type:"REPLY",callId:e.id,isError:!0,...e.returnValueIsError?{value:e.returnValue,isSerializedErrorInstance:!0}:{value:e.returnValue}};throw z(e)},Ee=e=>{if(O(e))return{penpal:"synAck",methodNames:e.methodPaths.map(F)};if($(e))return{penpal:"call",id:e.id,methodName:F(e.methodPath),args:e.args};if(V(e))return e.isError?{penpal:"reply",id:e.callId,resolution:"rejected",...e.isSerializedErrorInstance?{returnValue:e.value,returnValueIsError:!0}:{returnValue:e.value}}:{penpal:"reply",id:e.callId,resolution:"fulfilled",returnValue:e.value};throw z(e)},Ie=({messenger:e,methods:t,timeout:r,channel:n,log:s})=>{let l=Y(),f,v=[],c=!1,o=W(t),{promise:a,resolve:M,reject:d}=pe(),h=r!==void 0?setTimeout(()=>{d(new u("CONNECTION_TIMEOUT",`Connection timed out after ${r}ms`))},r):void 0,_=()=>{for(let i of v)i()},A=()=>{if(c)return;v.push(oe(e,t,n,s));let{remoteProxy:i,destroy:y}=ue(e,n,s);v.push(y),clearTimeout(h),c=!0,M({remoteProxy:i,destroy:_})},S=()=>{let i={namespace:p,type:"SYN",channel:n,participantId:l};s?.("Sending handshake SYN",i);try{e.sendMessage(i)}catch(y){d(new u("TRANSMISSION_FAILED",y.message))}},I=i=>{if(s?.("Received handshake SYN",i),i.participantId===f&&f!==T||(f=i.participantId,S(),!(l>f||f===T)))return;let m={namespace:p,channel:n,type:"ACK1",methodPaths:o};s?.("Sending handshake ACK1",m);try{e.sendMessage(m)}catch(X){d(new u("TRANSMISSION_FAILED",X.message));return}},P=i=>{s?.("Received handshake ACK1",i);let y={namespace:p,channel:n,type:"ACK2"};s?.("Sending handshake ACK2",y);try{e.sendMessage(y)}catch(m){d(new u("TRANSMISSION_FAILED",m.message));return}A()},E=i=>{s?.("Received handshake ACK2",i),A()},b=i=>{N(i)&&I(i),O(i)&&P(i),R(i)&&E(i)};return e.addMessageHandler(b),v.push(()=>e.removeMessageHandler(b)),S(),a},we=Ie,Ae=e=>{let t=!1,r;return(...n)=>(t||(t=!0,r=e(...n)),r)},Se=Ae,x=new WeakSet,me=({messenger:e,methods:t={},timeout:r,channel:n,log:s})=>{if(!e)throw new u("INVALID_ARGUMENT","messenger must be defined");if(x.has(e))throw new u("INVALID_ARGUMENT","A messenger can only be used for a single connection");x.add(e);let l=[e.destroy],f=Se(o=>{if(o){let a={namespace:p,channel:n,type:"DESTROY"};try{e.sendMessage(a)}catch{}}for(let a of l)a();s?.("Connection destroyed")}),v=o=>re(o)&&o.channel===n;return{promise:(async()=>{try{e.initialize({log:s,validateReceivedMessage:v}),e.addMessageHandler(M=>{ne(M)&&f(!1)});let{remoteProxy:o,destroy:a}=await we({messenger:e,methods:t,timeout:r,channel:n,log:s});return l.push(a),o}catch(o){throw f(!0),o}})(),destroy:()=>{f(!0)}}},K=me,Ne=class{#t;#s;#r;#i;#a;#n=new Set;#e;#o=!1;constructor({remoteWindow:e,allowedOrigins:t}){if(!e)throw new u("INVALID_ARGUMENT","remoteWindow must be defined");this.#t=e,this.#s=t?.length?t:[window.origin]}initialize=({log:e,validateReceivedMessage:t})=>{this.#r=e,this.#i=t,window.addEventListener("message",this.#h)};sendMessage=(e,t)=>{if(N(e)){let r=this.#d(e);this.#t.postMessage(e,{targetOrigin:r,transfer:t});return}if(O(e)||this.#o){let r=this.#o?Ee(e):e,n=this.#d(e);this.#t.postMessage(r,{targetOrigin:n,transfer:t});return}if(R(e)){let{port1:r,port2:n}=new MessageChannel;this.#e=r,r.addEventListener("message",this.#l),r.start();let s=[n,...t||[]],l=this.#d(e);this.#t.postMessage(e,{targetOrigin:l,transfer:s});return}if(this.#e){this.#e.postMessage(e,{transfer:t});return}throw new u("TRANSMISSION_FAILED","Cannot send message because the MessagePort is not connected")};addMessageHandler=e=>{this.#n.add(e)};removeMessageHandler=e=>{this.#n.delete(e)};destroy=()=>{window.removeEventListener("message",this.#h),this.#c(),this.#n.clear()};#u=e=>this.#s.some(t=>t instanceof RegExp?t.test(e):t===e||t==="*");#d=e=>{if(N(e))return"*";if(!this.#a)throw new u("TRANSMISSION_FAILED","Cannot send message because the remote origin is not established");return this.#a==="null"&&this.#s.includes("*")?"*":this.#a};#c=()=>{this.#e?.removeEventListener("message",this.#l),this.#e?.close(),this.#e=void 0};#h=({source:e,origin:t,ports:r,data:n})=>{if(e===this.#t){if(ve(n)){this.#r?.("Please upgrade the child window to the latest version of Penpal."),this.#o=!0;try{n=ye(n)}catch(s){this.#r?.(`Failed to translate deprecated message: ${s.message}`);return}}if(this.#i?.(n)){if(!this.#u(t)){this.#r?.(`Received a message from origin \`${t}\` which did not match allowed origins \`[${this.#s.join(", ")}]\``);return}if(N(n)&&(this.#c(),this.#a=t),R(n)&&!this.#o){if(this.#e=r[0],!this.#e){this.#r?.("Ignoring ACK2 because it did not include a MessagePort");return}this.#e.addEventListener("message",this.#l),this.#e.start()}for(let s of this.#n)s(n)}}};#l=({data:e})=>{if(this.#i?.(e))for(let t of this.#n)t(e)}},G=Ne;var w=new Map,L=K({messenger:new G({remoteWindow:window.parent,allowedOrigins:["__AllowedOrigins__"]}),channel:"iframe-bridge-channel",methods:{onMessage(e,t){let r=w.get(e);if(r)return r.forEach(n=>n(t)),!0}}}),Le=new Proxy({},{get(e,t){return async(...r)=>await L.promise.then(n=>n[t](...r))}}),De=(e,t,r)=>{let n=r?(l=>{t(l),J(e,n)}):t,s=w.get(e);return s?s.add(n):w.set(e,new Set([n])),()=>J(e,n)},J=(e,t)=>{if(!t){w.delete(e);return}let r=w.get(e);r&&r.delete(t)},D=!1;L.promise.then(()=>{D=!0});var be=()=>D,ke=e=>{if(D){e();return}L.promise.then(()=>{e()})};export{Le as default,be as isInit,J as offMessage,ke as onInit,De as onMessage};
1
+ var ee=class extends Error{code;constructor(e,t){super(t),this.name="PenpalError",this.code=e}},y=ee,te=e=>({name:e.name,message:e.message,stack:e.stack,penpalCode:e instanceof y?e.code:void 0}),re=({name:e,message:t,stack:n,penpalCode:r})=>{let i=r?new y(r,t):new Error(t);return i.name=e,i.stack=n,i},ne=Symbol("Reply"),se=class{value;transferables;#t=ne;constructor(e,t){this.value=e,this.transferables=t?.transferables}},ie=se,g="penpal",R=e=>typeof e=="object"&&e!==null,W=e=>typeof e=="function",oe=e=>R(e)&&e.namespace===g,C=e=>e.type==="SYN",_=e=>e.type==="ACK1",P=e=>e.type==="ACK2",$=e=>e.type==="CALL",U=e=>e.type==="REPLY",ae=e=>e.type==="DESTROY",z=(e,t=[])=>{let n=[];for(let r of Object.keys(e)){let i=e[r];W(i)?n.push([...t,r]):R(i)&&n.push(...z(i,[...t,r]))}return n},ce=(e,t)=>{let n=e.reduce((r,i)=>R(r)?r[i]:void 0,t);return W(n)?n:void 0},m=e=>e.join("."),F=(e,t,n)=>({namespace:g,channel:e,type:"REPLY",callId:t,isError:!0,...n instanceof Error?{value:te(n),isSerializedErrorInstance:!0}:{value:n}}),de=(e,t,n,r)=>{let i=!1,h=async d=>{if(i||!$(d))return;r?.(`Received ${m(d.methodPath)}() call`,d);let{methodPath:f,args:p,id:l}=d,s,a;try{let c=ce(f,t);if(!c)throw new y("METHOD_NOT_FOUND",`Method \`${m(f)}\` is not found.`);let o=await c(...p);o instanceof ie&&(a=o.transferables,o=await o.value),s={namespace:g,channel:n,type:"REPLY",callId:l,value:o}}catch(c){s=F(n,l,c)}if(!i)try{r?.(`Sending ${m(f)}() reply`,s),e.sendMessage(s,a)}catch(c){throw c.name==="DataCloneError"&&(s=F(n,l,c),r?.(`Sending ${m(f)}() reply`,s),e.sendMessage(s)),c}};return e.addMessageHandler(h),()=>{i=!0,e.removeMessageHandler(h)}},le=de,V=crypto.randomUUID?.bind(crypto)??(()=>new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")),he=Symbol("CallOptions"),ue=class{transferables;timeout;#t=he;constructor(e){this.transferables=e?.transferables,this.timeout=e?.timeout}},fe=ue,pe=new Set(["apply","call","bind"]),Y=(e,t,n=[])=>new Proxy(n.length?()=>{}:Object.create(null),{get(r,i){if(i!=="then")return n.length&&pe.has(i)?Reflect.get(r,i):Y(e,t,[...n,i])},apply(r,i,h){return e(n,h)}}),H=e=>new y("CONNECTION_DESTROYED",`Method call ${m(e)}() failed due to destroyed connection`),ye=(e,t,n)=>{let r=!1,i=new Map,h=p=>{if(!U(p))return;let{callId:l,value:s,isError:a,isSerializedErrorInstance:c}=p,o=i.get(l);o&&(i.delete(l),n?.(`Received ${m(o.methodPath)}() call`,p),a?o.reject(c?re(s):s):o.resolve(s))};return e.addMessageHandler(h),{remoteProxy:Y((p,l)=>{if(r)throw H(p);let s=V(),a=l[l.length-1],c=a instanceof fe,{timeout:o,transferables:M}=c?a:{},v=c?l.slice(0,-1):l;return new Promise((b,E)=>{let A=o!==void 0?window.setTimeout(()=>{i.delete(s),E(new y("METHOD_CALL_TIMEOUT",`Method call ${m(p)}() timed out after ${o}ms`))},o):void 0;i.set(s,{methodPath:p,resolve:b,reject:E,timeoutId:A});try{let I={namespace:g,channel:t,type:"CALL",id:s,methodPath:p,args:v};n?.(`Sending ${m(p)}() call`,I),e.sendMessage(I,M)}catch(I){E(new y("TRANSMISSION_FAILED",I.message))}})},n),destroy:()=>{r=!0,e.removeMessageHandler(h);for(let{methodPath:p,reject:l,timeoutId:s}of i.values())clearTimeout(s),l(H(p));i.clear()}}},ge=ye,Me=()=>{let e,t;return{promise:new Promise((r,i)=>{e=r,t=i}),resolve:e,reject:t}},ve=Me,S="deprecated-penpal",me=e=>R(e)&&"penpal"in e,we=e=>e.split("."),x=e=>e.join("."),be=e=>{try{return JSON.stringify(e)}catch{return String(e)}},B=e=>new y("TRANSMISSION_FAILED",`Unexpected message to translate: ${be(e)}`),Ie=e=>{if(e.penpal==="syn")return{namespace:g,channel:void 0,type:"SYN",participantId:S};if(e.penpal==="ack")return{namespace:g,channel:void 0,type:"ACK2"};if(e.penpal==="call")return{namespace:g,channel:void 0,type:"CALL",id:e.id,methodPath:we(e.methodName),args:e.args};if(e.penpal==="reply")return e.resolution==="fulfilled"?{namespace:g,channel:void 0,type:"REPLY",callId:e.id,value:e.returnValue}:{namespace:g,channel:void 0,type:"REPLY",callId:e.id,isError:!0,...e.returnValueIsError?{value:e.returnValue,isSerializedErrorInstance:!0}:{value:e.returnValue}};throw B(e)},Ee=e=>{if(_(e))return{penpal:"synAck",methodNames:e.methodPaths.map(x)};if($(e))return{penpal:"call",id:e.id,methodName:x(e.methodPath),args:e.args};if(U(e))return e.isError?{penpal:"reply",id:e.callId,resolution:"rejected",...e.isSerializedErrorInstance?{returnValue:e.value,returnValueIsError:!0}:{returnValue:e.value}}:{penpal:"reply",id:e.callId,resolution:"fulfilled",returnValue:e.value};throw B(e)},ke=({messenger:e,methods:t,timeout:n,channel:r,log:i})=>{let h=V(),d,f=[],p=!1,l=z(t),{promise:s,resolve:a,reject:c}=ve(),o=n!==void 0?setTimeout(()=>{c(new y("CONNECTION_TIMEOUT",`Connection timed out after ${n}ms`))},n):void 0,M=()=>{for(let u of f)u()},v=()=>{if(p)return;f.push(le(e,t,r,i));let{remoteProxy:u,destroy:w}=ge(e,r,i);f.push(w),clearTimeout(o),p=!0,a({remoteProxy:u,destroy:M})},b=()=>{let u={namespace:g,type:"SYN",channel:r,participantId:h};i?.("Sending handshake SYN",u);try{e.sendMessage(u)}catch(w){c(new y("TRANSMISSION_FAILED",w.message))}},E=u=>{if(i?.("Received handshake SYN",u),u.participantId===d&&d!==S||(d=u.participantId,b(),!(h>d||d===S)))return;let k={namespace:g,channel:r,type:"ACK1",methodPaths:l};i?.("Sending handshake ACK1",k);try{e.sendMessage(k)}catch(Z){c(new y("TRANSMISSION_FAILED",Z.message));return}},A=u=>{i?.("Received handshake ACK1",u);let w={namespace:g,channel:r,type:"ACK2"};i?.("Sending handshake ACK2",w);try{e.sendMessage(w)}catch(k){c(new y("TRANSMISSION_FAILED",k.message));return}v()},I=u=>{i?.("Received handshake ACK2",u),v()},D=u=>{C(u)&&E(u),_(u)&&A(u),P(u)&&I(u)};return e.addMessageHandler(D),f.push(()=>e.removeMessageHandler(D)),b(),s},Ce=ke,Re=e=>{let t=!1,n;return(...r)=>(t||(t=!0,n=e(...r)),n)},Ae=Re,j=new WeakSet,Pe=({messenger:e,methods:t={},timeout:n,channel:r,log:i})=>{if(!e)throw new y("INVALID_ARGUMENT","messenger must be defined");if(j.has(e))throw new y("INVALID_ARGUMENT","A messenger can only be used for a single connection");j.add(e);let h=[e.destroy],d=Ae(l=>{if(l){let s={namespace:g,channel:r,type:"DESTROY"};try{e.sendMessage(s)}catch{}}for(let s of h)s();i?.("Connection destroyed")}),f=l=>oe(l)&&l.channel===r;return{promise:(async()=>{try{e.initialize({log:i,validateReceivedMessage:f}),e.addMessageHandler(a=>{ae(a)&&d(!1)});let{remoteProxy:l,destroy:s}=await Ce({messenger:e,methods:t,timeout:n,channel:r,log:i});return h.push(s),l}catch(l){throw d(!0),l}})(),destroy:()=>{d(!0)}}},K=Pe,Se=class{#t;#s;#r;#a;#i;#n=new Set;#e;#o=!1;constructor({remoteWindow:e,allowedOrigins:t}){if(!e)throw new y("INVALID_ARGUMENT","remoteWindow must be defined");this.#t=e,this.#s=t?.length?t:[window.origin]}initialize=({log:e,validateReceivedMessage:t})=>{this.#r=e,this.#a=t,window.addEventListener("message",this.#h)};sendMessage=(e,t)=>{if(C(e)){let n=this.#c(e);this.#t.postMessage(e,{targetOrigin:n,transfer:t});return}if(_(e)||this.#o){let n=this.#o?Ee(e):e,r=this.#c(e);this.#t.postMessage(n,{targetOrigin:r,transfer:t});return}if(P(e)){let{port1:n,port2:r}=new MessageChannel;this.#e=n,n.addEventListener("message",this.#d),n.start();let i=[r,...t||[]],h=this.#c(e);this.#t.postMessage(e,{targetOrigin:h,transfer:i});return}if(this.#e){this.#e.postMessage(e,{transfer:t});return}throw new y("TRANSMISSION_FAILED","Cannot send message because the MessagePort is not connected")};addMessageHandler=e=>{this.#n.add(e)};removeMessageHandler=e=>{this.#n.delete(e)};destroy=()=>{window.removeEventListener("message",this.#h),this.#l(),this.#n.clear()};#u=e=>this.#s.some(t=>t instanceof RegExp?t.test(e):t===e||t==="*");#c=e=>{if(C(e))return"*";if(!this.#i)throw new y("TRANSMISSION_FAILED","Cannot send message because the remote origin is not established");return this.#i==="null"&&this.#s.includes("*")?"*":this.#i};#l=()=>{this.#e?.removeEventListener("message",this.#d),this.#e?.close(),this.#e=void 0};#h=({source:e,origin:t,ports:n,data:r})=>{if(e===this.#t){if(me(r)){this.#r?.("Please upgrade the child window to the latest version of Penpal."),this.#o=!0;try{r=Ie(r)}catch(i){this.#r?.(`Failed to translate deprecated message: ${i.message}`);return}}if(this.#a?.(r)){if(!this.#u(t)){this.#r?.(`Received a message from origin \`${t}\` which did not match allowed origins \`[${this.#s.join(", ")}]\``);return}if(C(r)&&(this.#l(),this.#i=t),P(r)&&!this.#o){if(this.#e=n[0],!this.#e){this.#r?.("Ignoring ACK2 because it did not include a MessagePort");return}this.#e.addEventListener("message",this.#d),this.#e.start()}for(let i of this.#n)i(r)}}};#d=({data:e})=>{if(this.#a?.(e))for(let t of this.#n)t(e)}},G=Se;var X="iframe-bridge-kit.callback",T="__iframeBridgeInvokeCallback",N=e=>typeof e=="object"&&e!==null,J=e=>{let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},_e=e=>N(e)&&e.__iframeBridgeType===X&&typeof e.id=="string",q=e=>{let t=new Map,n=new WeakMap,r=new Map,i=0,h=s=>{let a=n.get(s);return a||(a=`${Date.now().toString(36)}-${(++i).toString(36)}`,n.set(s,a)),t.set(a,s),{__iframeBridgeType:X,id:a}},d=(s,a=new WeakMap)=>{if(typeof s=="function")return h(s);if(!N(s))return s;if(a.has(s))return a.get(s);if(Array.isArray(s)){let o=[];return a.set(s,o),s.forEach((M,v)=>{o[v]=d(M,a)}),o}if(!J(s))return s;let c={};return a.set(s,c),Object.keys(s).forEach(o=>{c[o]=d(s[o],a)}),c},f=(s,a=new WeakMap)=>{if(!N(s))return s;if(_e(s)){let o=r.get(s.id);return o||(o=async(...M)=>{let b=await(await e())[T](s.id,d(M));return f(b)},r.set(s.id,o)),o}if(a.has(s))return a.get(s);if(Array.isArray(s)){let o=[];return a.set(s,o),s.forEach((M,v)=>{o[v]=f(M,a)}),o}if(!J(s))return s;let c={};return a.set(s,c),Object.keys(s).forEach(o=>{c[o]=f(s[o],a)}),c};return{serialize:d,deserialize:f,invokeLocalCallback:async(s,a)=>{let c=t.get(s);if(!c)throw new Error(`Callback "${s}" is not available`);let o=await c(...f(a));return d(o)},clearCallbacks:()=>{t.clear(),r.clear()}}};var Ne="iframe-bridge-channel",Q=["__AllowedOrigins__"],L=class{msgProxy=new Map;initCallbacks=new Set;conn;remotePromise;inited=!1;connectionId=0;callbacks=q(()=>this.getRemote());api;constructor(){let t={onMessage:this.onMessage,offMessage:this.offMessage,isInit:this.isInit,onInit:this.onInit,destroy:this.destroy};this.api=new Proxy({},{get:(n,r)=>{if(r!=="then"&&typeof r=="string")return r in t?t[r]:async(...i)=>{let d=(await this.getRemote())[r];if(typeof d!="function")throw new Error(`Remote method "${String(r)}" is not available`);return this.callbacks.deserialize(await d(...this.callbacks.serialize(i)))}}})}connect=(t,n=Q)=>{this.conn?.destroy(),this.inited=!1;let r=++this.connectionId;return this.conn=K({messenger:new G({remoteWindow:t,allowedOrigins:n}),channel:Ne,methods:{onMessage:this.receiveMessage,[T]:this.callbacks.invokeLocalCallback}}),this.remotePromise=this.conn.promise.then(i=>(r===this.connectionId&&(this.inited=!0,this.notifyInit()),i)),this.remotePromise};getRemote=()=>{if(!this.remotePromise){if(typeof window>"u"||window.parent===window)throw new Error("remoteWindow is not configured. Pass a Window when creating the bridge client.");return this.connect(window.parent)}return this.remotePromise};receiveMessage=(t,n)=>{let r=this.msgProxy.get(t);if(r){let i=this.callbacks.deserialize(n);return r.forEach(h=>h(i)),!0}};notifyInit=()=>{let t=Array.from(this.initCallbacks);this.initCallbacks.clear(),t.forEach(n=>n(this.api))};onMessage=(t,n,r)=>{let i=r?(d=>{n(d),this.offMessage(t,i)}):n,h=this.msgProxy.get(t);return h?h.add(i):this.msgProxy.set(t,new Set([i])),()=>this.offMessage(t,i)};offMessage=(t,n)=>{if(!n){this.msgProxy.delete(t);return}let r=this.msgProxy.get(t);r&&r.delete(n)};isInit=()=>this.inited;onInit=t=>{if(this.inited){t(this.api);return}this.initCallbacks.add(t)};destroy=()=>{this.connectionId++,this.conn?.destroy(),this.conn=void 0,this.remotePromise=void 0,this.inited=!1,this.initCallbacks.clear(),this.callbacks.clearCallbacks()}},Te=(e,t=Q)=>{let n=new L;return n.connect(e,t),n.api},O,$e=()=>(O||(O=Te(window.parent)),O);export{Te as createBridgeClient,$e as default};
package/dist/index.d.mts CHANGED
@@ -1,17 +1,8 @@
1
- type BridgeEventArgs<T> = [T] extends [void] ? [data?: T] : [data: T];
2
- type BridgeMethods = Record<string, (...args: any[]) => any>;
3
- /** 在父窗口定义 bridge,暴露方法给 iframe 调用 */
4
- declare const defineBridge: <TEmit extends object = Record<string, unknown>>(name: string, methods: BridgeMethods) => {
5
- create(iframe: HTMLIFrameElement, allowedOrigins?: string[]): Promise<{
6
- emit<T extends keyof TEmit>(type: T, ...args: BridgeEventArgs<TEmit[T]>): void;
7
- }>;
8
- };
9
- /** 在 iframe 中定义 bridge,暴露方法给父窗口调用 */
10
- declare const defineIframeBridge: <TEmit extends object = Record<string, unknown>>(name: string, methods: BridgeMethods) => {
11
- connect(allowedOrigins?: string[]): Promise<{
12
- emit<T extends keyof TEmit>(type: T, ...args: BridgeEventArgs<TEmit[T]>): void;
13
- destroy: () => void;
14
- }>;
15
- };
1
+ type BridgeTarget = HTMLIFrameElement | Window;
2
+ /** 定义一个 bridge,暴露方法给 iframe 父/子窗口调用 */
3
+ declare const defineBridge: <TEmit extends Record<string, object | string | number | boolean | null | undefined>>(name: string, methods: Record<string, (...args: any[]) => any>) => (target: BridgeTarget, allowedOrigins?: string[]) => Promise<{
4
+ emit<T extends keyof TEmit>(type: T, data: TEmit[T]): void;
5
+ destroy(): void;
6
+ }>;
16
7
 
17
- export { defineBridge, defineIframeBridge };
8
+ export { defineBridge };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,8 @@
1
- type BridgeEventArgs<T> = [T] extends [void] ? [data?: T] : [data: T];
2
- type BridgeMethods = Record<string, (...args: any[]) => any>;
3
- /** 在父窗口定义 bridge,暴露方法给 iframe 调用 */
4
- declare const defineBridge: <TEmit extends object = Record<string, unknown>>(name: string, methods: BridgeMethods) => {
5
- create(iframe: HTMLIFrameElement, allowedOrigins?: string[]): Promise<{
6
- emit<T extends keyof TEmit>(type: T, ...args: BridgeEventArgs<TEmit[T]>): void;
7
- }>;
8
- };
9
- /** 在 iframe 中定义 bridge,暴露方法给父窗口调用 */
10
- declare const defineIframeBridge: <TEmit extends object = Record<string, unknown>>(name: string, methods: BridgeMethods) => {
11
- connect(allowedOrigins?: string[]): Promise<{
12
- emit<T extends keyof TEmit>(type: T, ...args: BridgeEventArgs<TEmit[T]>): void;
13
- destroy: () => void;
14
- }>;
15
- };
1
+ type BridgeTarget = HTMLIFrameElement | Window;
2
+ /** 定义一个 bridge,暴露方法给 iframe 父/子窗口调用 */
3
+ declare const defineBridge: <TEmit extends Record<string, object | string | number | boolean | null | undefined>>(name: string, methods: Record<string, (...args: any[]) => any>) => (target: BridgeTarget, allowedOrigins?: string[]) => Promise<{
4
+ emit<T extends keyof TEmit>(type: T, data: TEmit[T]): void;
5
+ destroy(): void;
6
+ }>;
16
7
 
17
- export { defineBridge, defineIframeBridge };
8
+ export { defineBridge };
package/dist/index.js CHANGED
@@ -20,59 +20,173 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- defineBridge: () => defineBridge,
24
- defineIframeBridge: () => defineIframeBridge
23
+ defineBridge: () => defineBridge
25
24
  });
26
25
  module.exports = __toCommonJS(index_exports);
27
26
  var import_penpal = require("penpal");
28
- var defineBridge = (name, methods) => {
29
- return {
30
- async create(iframe, allowedOrigins = ["*"]) {
31
- if (!iframe.contentWindow) {
32
- throw new Error("iframe contentWindow is null");
27
+
28
+ // src/callbacks.ts
29
+ var callbackRefType = "iframe-bridge-kit.callback";
30
+ var callbackInvokeMethod = "__iframeBridgeInvokeCallback";
31
+ var isObject = (value) => typeof value === "object" && value !== null;
32
+ var isPlainObject = (value) => {
33
+ const proto = Object.getPrototypeOf(value);
34
+ return proto === Object.prototype || proto === null;
35
+ };
36
+ var isCallbackDescriptor = (value) => {
37
+ return isObject(value) && value.__iframeBridgeType === callbackRefType && typeof value.id === "string";
38
+ };
39
+ var createCallbackBridge = (getRemote) => {
40
+ const localCallbacks = /* @__PURE__ */ new Map();
41
+ const localCallbackIds = /* @__PURE__ */ new WeakMap();
42
+ const remoteCallbacks = /* @__PURE__ */ new Map();
43
+ let nextCallbackId = 0;
44
+ const registerLocalCallback = (fn) => {
45
+ let id = localCallbackIds.get(fn);
46
+ if (!id) {
47
+ id = `${Date.now().toString(36)}-${(++nextCallbackId).toString(36)}`;
48
+ localCallbackIds.set(fn, id);
49
+ }
50
+ localCallbacks.set(id, fn);
51
+ return {
52
+ __iframeBridgeType: callbackRefType,
53
+ id
54
+ };
55
+ };
56
+ const serialize = (value, seen = /* @__PURE__ */ new WeakMap()) => {
57
+ if (typeof value === "function") {
58
+ return registerLocalCallback(value);
59
+ }
60
+ if (!isObject(value)) {
61
+ return value;
62
+ }
63
+ if (seen.has(value)) {
64
+ return seen.get(value);
65
+ }
66
+ if (Array.isArray(value)) {
67
+ const copy2 = [];
68
+ seen.set(value, copy2);
69
+ value.forEach((item, index) => {
70
+ copy2[index] = serialize(item, seen);
71
+ });
72
+ return copy2;
73
+ }
74
+ if (!isPlainObject(value)) {
75
+ return value;
76
+ }
77
+ const copy = {};
78
+ seen.set(value, copy);
79
+ Object.keys(value).forEach((key) => {
80
+ copy[key] = serialize(value[key], seen);
81
+ });
82
+ return copy;
83
+ };
84
+ const deserialize = (value, seen = /* @__PURE__ */ new WeakMap()) => {
85
+ if (!isObject(value)) {
86
+ return value;
87
+ }
88
+ if (isCallbackDescriptor(value)) {
89
+ let callback = remoteCallbacks.get(value.id);
90
+ if (!callback) {
91
+ callback = async (...args) => {
92
+ const remote = await getRemote();
93
+ const result = await remote[callbackInvokeMethod](value.id, serialize(args));
94
+ return deserialize(result);
95
+ };
96
+ remoteCallbacks.set(value.id, callback);
33
97
  }
34
- const conn = (0, import_penpal.connect)({
35
- messenger: new import_penpal.WindowMessenger({
36
- remoteWindow: iframe.contentWindow,
37
- allowedOrigins
38
- }),
39
- channel: "iframe-bridge-channel",
40
- methods
98
+ return callback;
99
+ }
100
+ if (seen.has(value)) {
101
+ return seen.get(value);
102
+ }
103
+ if (Array.isArray(value)) {
104
+ const copy2 = [];
105
+ seen.set(value, copy2);
106
+ value.forEach((item, index) => {
107
+ copy2[index] = deserialize(item, seen);
41
108
  });
42
- const remote = await conn.promise;
43
- return {
44
- emit(type, ...args) {
45
- const [data] = args;
46
- remote.onMessage(type, data);
47
- }
48
- };
109
+ return copy2;
49
110
  }
111
+ if (!isPlainObject(value)) {
112
+ return value;
113
+ }
114
+ const copy = {};
115
+ seen.set(value, copy);
116
+ Object.keys(value).forEach((key) => {
117
+ copy[key] = deserialize(value[key], seen);
118
+ });
119
+ return copy;
120
+ };
121
+ const invokeLocalCallback = async (id, args) => {
122
+ const callback = localCallbacks.get(id);
123
+ if (!callback) {
124
+ throw new Error(`Callback "${id}" is not available`);
125
+ }
126
+ const result = await callback(...deserialize(args));
127
+ return serialize(result);
128
+ };
129
+ const clearCallbacks = () => {
130
+ localCallbacks.clear();
131
+ remoteCallbacks.clear();
50
132
  };
51
- };
52
- var defineIframeBridge = (name, methods) => {
53
133
  return {
54
- async connect(allowedOrigins = ["*"]) {
55
- const conn = (0, import_penpal.connect)({
56
- messenger: new import_penpal.WindowMessenger({
57
- remoteWindow: window.parent,
58
- allowedOrigins
59
- }),
60
- channel: "iframe-bridge-channel",
61
- methods
62
- });
63
- const remote = await conn.promise;
64
- return {
65
- emit(type, ...args) {
66
- const [data] = args;
67
- remote.onMessage(type, data);
68
- },
69
- destroy: conn.destroy
70
- };
134
+ serialize,
135
+ deserialize,
136
+ invokeLocalCallback,
137
+ clearCallbacks
138
+ };
139
+ };
140
+
141
+ // src/index.ts
142
+ var resolveRemoteWindow = (target) => {
143
+ if (typeof HTMLIFrameElement !== "undefined" && target instanceof HTMLIFrameElement) {
144
+ if (!target.contentWindow) {
145
+ throw new Error("remoteWindow is null");
71
146
  }
147
+ return target.contentWindow;
148
+ }
149
+ return target;
150
+ };
151
+ var defineBridge = (name, methods) => {
152
+ return async (target, allowedOrigins = ["*"]) => {
153
+ const remoteWindow = resolveRemoteWindow(target);
154
+ let remotePromise;
155
+ const callbacks = createCallbackBridge(() => remotePromise);
156
+ const bridgeMethods = Object.keys(methods).reduce((wrapped, methodName) => {
157
+ if (methodName === callbackInvokeMethod) {
158
+ throw new Error(`Method name "${callbackInvokeMethod}" is reserved by iframe-bridge-kit`);
159
+ }
160
+ wrapped[methodName] = async (...args) => {
161
+ const result = await methods[methodName].apply(methods, callbacks.deserialize(args));
162
+ return callbacks.serialize(result);
163
+ };
164
+ return wrapped;
165
+ }, {
166
+ [callbackInvokeMethod]: callbacks.invokeLocalCallback
167
+ });
168
+ const conn = (0, import_penpal.connect)({
169
+ messenger: new import_penpal.WindowMessenger({
170
+ remoteWindow,
171
+ allowedOrigins
172
+ }),
173
+ channel: "iframe-bridge-channel",
174
+ methods: bridgeMethods
175
+ });
176
+ remotePromise = conn.promise;
177
+ const remote = await remotePromise;
178
+ return {
179
+ emit(type, data) {
180
+ return remote.onMessage(type, callbacks.serialize(data));
181
+ },
182
+ destroy() {
183
+ callbacks.clearCallbacks();
184
+ conn.destroy();
185
+ }
186
+ };
72
187
  };
73
188
  };
74
189
  // Annotate the CommonJS export names for ESM import in node:
75
190
  0 && (module.exports = {
76
- defineBridge,
77
- defineIframeBridge
191
+ defineBridge
78
192
  });