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 +38 -76
- package/dist/full/core.js +1 -1
- package/dist/full/core.mjs +1 -1
- package/dist/index.d.mts +7 -16
- package/dist/index.d.ts +7 -16
- package/dist/index.js +156 -42
- package/dist/index.mjs +155 -40
- package/dist/mini/core.js +1 -1
- package/dist/mini/core.mjs +1 -1
- package/dist/vite.js +46 -84
- package/dist/vite.mjs +46 -84
- package/package.json +1 -1
- package/dist/full/parent-core.js +0 -1
- package/dist/full/parent-core.mjs +0 -1
- package/dist/mini/parent-core.js +0 -1
- package/dist/mini/parent-core.mjs +0 -1
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
|
|
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
|
-
* 🚀 **
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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\.
|
|
126
|
+
### 3\. Reverse Calls: Child Defines API, Parent Calls It
|
|
125
127
|
|
|
126
|
-
If
|
|
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
|
-
//
|
|
130
|
-
import {
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
return
|
|
134
|
+
export const childBridge = defineBridge('child-api', {
|
|
135
|
+
async getSelection() {
|
|
136
|
+
return window.getSelection()?.toString() || ''
|
|
148
137
|
}
|
|
149
138
|
})
|
|
150
139
|
|
|
151
|
-
childBridge.
|
|
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
|
-
//
|
|
160
|
-
import
|
|
144
|
+
// parent window
|
|
145
|
+
import { createBridgeClient } from '../bridges/child-api'
|
|
161
146
|
|
|
162
|
-
const
|
|
163
|
-
const child = ChildBridge.create(iframe)
|
|
147
|
+
const iframeEl = document.querySelector('iframe')!
|
|
164
148
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
173
|
-
|
|
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(
|
|
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
|
|
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` (
|
|
232
|
-
* **`
|
|
233
|
-
* **`
|
|
234
|
-
* **`
|
|
235
|
-
* **`
|
|
236
|
-
|
|
237
|
-
|
|
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. **
|
|
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});
|
package/dist/full/core.mjs
CHANGED
|
@@ -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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
8
|
+
export { defineBridge };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
});
|