chrome-in-iframe 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/README.zh-CN.md +225 -0
- package/dist/api.d.ts +35 -0
- package/dist/channel/channel.d.ts +3 -0
- package/dist/channel/deserializer.d.ts +2 -0
- package/dist/channel/listener.d.ts +3 -0
- package/dist/channel/path.d.ts +10 -0
- package/dist/channel/sender.d.ts +2 -0
- package/dist/channel/serializer.d.ts +2 -0
- package/dist/channel/types.d.ts +93 -0
- package/dist/channel/utils.d.ts +5 -0
- package/dist/client/context.d.ts +6 -0
- package/dist/client/endpoint.d.ts +12 -0
- package/dist/client/proxy.d.ts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1011 -0
- package/dist/processor/accessProperty.d.ts +10 -0
- package/dist/processor/invoke.d.ts +11 -0
- package/dist/processor/invokeCallback.d.ts +11 -0
- package/dist/processor/readProperty.d.ts +2 -0
- package/dist/processor/registry.d.ts +6 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# chrome-in-iframe
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/chrome-in-iframe)
|
|
4
|
+
|
|
5
|
+
[中文文档](./README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
Bridge Chrome extension APIs from a Chrome extension page into an embedded iframe.
|
|
8
|
+
|
|
9
|
+
This is useful when an extension page such as `options.html`, `popup.html`, or another `chrome-extension://...` page embeds an iframe, but the iframe itself cannot directly access Chrome extension APIs.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install chrome-in-iframe
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## How It Works
|
|
18
|
+
|
|
19
|
+
Use the package on both sides:
|
|
20
|
+
|
|
21
|
+
- In the Chrome extension page, call `exposeChromeInIframe()` and pass the iframe element.
|
|
22
|
+
- Inside the iframe, call `connectChromeInIframe()` and use the returned `chrome` proxy.
|
|
23
|
+
|
|
24
|
+
Method calls, promises, plain objects, errors, and callback functions are forwarded through `postMessage`.
|
|
25
|
+
|
|
26
|
+
## TypeScript Usage
|
|
27
|
+
|
|
28
|
+
### Extension Page
|
|
29
|
+
|
|
30
|
+
For example, in `options.html` or the script bundled for that page:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { exposeChromeInIframe } from 'chrome-in-iframe';
|
|
34
|
+
|
|
35
|
+
const iframe = document.querySelector<HTMLIFrameElement>('#app-frame');
|
|
36
|
+
|
|
37
|
+
if (!iframe) {
|
|
38
|
+
throw new Error('iframe not found');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const bridge = exposeChromeInIframe({
|
|
42
|
+
iframe,
|
|
43
|
+
key: 'my-extension-bridge',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
window.addEventListener('beforeunload', () => {
|
|
47
|
+
bridge.destroy();
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Iframe Page
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { connectChromeInIframe } from 'chrome-in-iframe';
|
|
55
|
+
|
|
56
|
+
const { chrome, get, destroy } = connectChromeInIframe({
|
|
57
|
+
key: 'my-extension-bridge',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const tabs = await chrome.tabs.query({
|
|
61
|
+
active: true,
|
|
62
|
+
currentWindow: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const extensionId = await get<string>('runtime.id');
|
|
66
|
+
|
|
67
|
+
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
|
68
|
+
console.log('tab updated', tabId, changeInfo);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
window.addEventListener('beforeunload', () => {
|
|
72
|
+
destroy();
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If your TypeScript project does not already load Chrome types, add them in the iframe app:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install -D @types/chrome
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"compilerOptions": {
|
|
85
|
+
"types": ["chrome"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## JavaScript Usage
|
|
91
|
+
|
|
92
|
+
### Extension Page
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import { exposeChromeInIframe } from 'chrome-in-iframe';
|
|
96
|
+
|
|
97
|
+
const iframe = document.querySelector('#app-frame');
|
|
98
|
+
|
|
99
|
+
const bridge = exposeChromeInIframe({
|
|
100
|
+
iframe,
|
|
101
|
+
key: 'my-extension-bridge',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
window.addEventListener('beforeunload', () => {
|
|
105
|
+
bridge.destroy();
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Iframe Page
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
import { connectChromeInIframe } from 'chrome-in-iframe';
|
|
113
|
+
|
|
114
|
+
const { chrome, get, destroy } = connectChromeInIframe({
|
|
115
|
+
key: 'my-extension-bridge',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const tabs = await chrome.tabs.query({
|
|
119
|
+
active: true,
|
|
120
|
+
currentWindow: true,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const extensionId = await get('runtime.id');
|
|
124
|
+
|
|
125
|
+
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
|
126
|
+
console.log('tab updated', tabId, changeInfo);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
window.addEventListener('beforeunload', () => {
|
|
130
|
+
destroy();
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Security Options
|
|
135
|
+
|
|
136
|
+
`targetOrigin` and `allowedOrigin` are optional. If omitted, messages are sent with `'*'` and incoming messages are only checked against the expected iframe or parent window source (via `MessageEvent.source`).
|
|
137
|
+
|
|
138
|
+
> **Warning**: When both sides are Chrome extension pages under the same `chrome-extension://` origin, the default behavior is safe. However, if your iframe loads a **third-party page** or a page from a **different origin**, leaving `allowedOrigin` unset means **any window** can send forged `postMessage` messages and impersonate Chrome API calls. In that case, always set `allowedOrigin` to the expected origin.
|
|
139
|
+
|
|
140
|
+
For production, pass origins when you know them.
|
|
141
|
+
|
|
142
|
+
### Extension Page
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
exposeChromeInIframe({
|
|
146
|
+
iframe,
|
|
147
|
+
key: 'my-extension-bridge',
|
|
148
|
+
targetOrigin: 'https://your-iframe-app.example',
|
|
149
|
+
allowedOrigin: 'https://your-iframe-app.example',
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Iframe Page
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const { chrome } = connectChromeInIframe({
|
|
157
|
+
key: 'my-extension-bridge',
|
|
158
|
+
targetOrigin: 'chrome-extension://modkelfkcfjpgbfmnbnllalkiogfofhb',
|
|
159
|
+
allowedOrigin: 'chrome-extension://modkelfkcfjpgbfmnbnllalkiogfofhb',
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Reading Properties
|
|
164
|
+
|
|
165
|
+
Chrome API methods can be called directly:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const tabs = await chrome.tabs.query({ active: true });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Readable properties should be read through `get()` or `$get()`:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const extensionId = await get<string>('runtime.id');
|
|
175
|
+
const platformOs = await chrome.runtime.$get<string>('PlatformOs');
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## API
|
|
179
|
+
|
|
180
|
+
### `exposeChromeInIframe(options)`
|
|
181
|
+
|
|
182
|
+
Runs in the Chrome extension page.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const bridge = exposeChromeInIframe({
|
|
186
|
+
iframe,
|
|
187
|
+
key,
|
|
188
|
+
targetOrigin,
|
|
189
|
+
allowedOrigin,
|
|
190
|
+
chromeApi,
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Options:
|
|
195
|
+
|
|
196
|
+
- `iframe`: the iframe element to bridge.
|
|
197
|
+
- `key`: optional shared bridge key. Use the same key on both sides.
|
|
198
|
+
- `targetOrigin`: optional origin used by `postMessage`.
|
|
199
|
+
- `allowedOrigin`: optional origin, origin list, or predicate used to filter incoming messages.
|
|
200
|
+
- `chromeApi`: optional custom Chrome-like object. Defaults to `globalThis.chrome`.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
|
|
204
|
+
- `proxy`: the bridged Chrome API proxy.
|
|
205
|
+
- `get(path)`: read a property from the bridged Chrome API.
|
|
206
|
+
- `destroy()`: remove message listeners and clear bridge state.
|
|
207
|
+
|
|
208
|
+
### `connectChromeInIframe(options)`
|
|
209
|
+
|
|
210
|
+
Runs inside the iframe.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
const { chrome, get, destroy } = connectChromeInIframe({
|
|
214
|
+
key,
|
|
215
|
+
targetOrigin,
|
|
216
|
+
allowedOrigin,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
|
|
222
|
+
- `chrome`: a proxy for the extension page's Chrome API.
|
|
223
|
+
- `proxy`: the same object as `chrome`.
|
|
224
|
+
- `get(path)`: read a property from the bridged Chrome API.
|
|
225
|
+
- `destroy()`: remove message listeners and clear bridge state.
|
|
226
|
+
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# chrome-in-iframe
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/chrome-in-iframe)
|
|
4
|
+
|
|
5
|
+
[English](./README.md)
|
|
6
|
+
|
|
7
|
+
将 Chrome 扩展页面的 Chrome 扩展 API 桥接到嵌入的 iframe 中。
|
|
8
|
+
|
|
9
|
+
当扩展页面(如 `options.html`、`popup.html` 或其他 `chrome-extension://...` 页面)嵌入了 iframe,但 iframe 本身无法直接访问 Chrome 扩展 API 时,这个库非常有用。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install chrome-in-iframe
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 工作原理
|
|
18
|
+
|
|
19
|
+
在两侧都使用该包:
|
|
20
|
+
|
|
21
|
+
- 在 Chrome 扩展页面中,调用 `exposeChromeInIframe()` 并传入 iframe 元素。
|
|
22
|
+
- 在 iframe 内部,调用 `connectChromeInIframe()` 并使用返回的 `chrome` 代理。
|
|
23
|
+
|
|
24
|
+
方法调用、Promise、普通对象、错误和回调函数通过 `postMessage` 进行转发。
|
|
25
|
+
|
|
26
|
+
## TypeScript 用法
|
|
27
|
+
|
|
28
|
+
### 扩展页面
|
|
29
|
+
|
|
30
|
+
例如在 `options.html` 或为该页面打包的脚本中:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { exposeChromeInIframe } from 'chrome-in-iframe';
|
|
34
|
+
|
|
35
|
+
const iframe = document.querySelector<HTMLIFrameElement>('#app-frame');
|
|
36
|
+
|
|
37
|
+
if (!iframe) {
|
|
38
|
+
throw new Error('iframe not found');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const bridge = exposeChromeInIframe({
|
|
42
|
+
iframe,
|
|
43
|
+
key: 'my-extension-bridge',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
window.addEventListener('beforeunload', () => {
|
|
47
|
+
bridge.destroy();
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Iframe 页面
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { connectChromeInIframe } from 'chrome-in-iframe';
|
|
55
|
+
|
|
56
|
+
const { chrome, get, destroy } = connectChromeInIframe({
|
|
57
|
+
key: 'my-extension-bridge',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const tabs = await chrome.tabs.query({
|
|
61
|
+
active: true,
|
|
62
|
+
currentWindow: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const extensionId = await get<string>('runtime.id');
|
|
66
|
+
|
|
67
|
+
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
|
68
|
+
console.log('tab updated', tabId, changeInfo);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
window.addEventListener('beforeunload', () => {
|
|
72
|
+
destroy();
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
如果你的 TypeScript 项目尚未加载 Chrome 类型,请在 iframe 应用中添加:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install -D @types/chrome
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"compilerOptions": {
|
|
85
|
+
"types": ["chrome"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## JavaScript 用法
|
|
91
|
+
|
|
92
|
+
### 扩展页面
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import { exposeChromeInIframe } from 'chrome-in-iframe';
|
|
96
|
+
|
|
97
|
+
const iframe = document.querySelector('#app-frame');
|
|
98
|
+
|
|
99
|
+
const bridge = exposeChromeInIframe({
|
|
100
|
+
iframe,
|
|
101
|
+
key: 'my-extension-bridge',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
window.addEventListener('beforeunload', () => {
|
|
105
|
+
bridge.destroy();
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Iframe 页面
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
import { connectChromeInIframe } from 'chrome-in-iframe';
|
|
113
|
+
|
|
114
|
+
const { chrome, get, destroy } = connectChromeInIframe({
|
|
115
|
+
key: 'my-extension-bridge',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const tabs = await chrome.tabs.query({
|
|
119
|
+
active: true,
|
|
120
|
+
currentWindow: true,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const extensionId = await get('runtime.id');
|
|
124
|
+
|
|
125
|
+
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
|
126
|
+
console.log('tab updated', tabId, changeInfo);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
window.addEventListener('beforeunload', () => {
|
|
130
|
+
destroy();
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 安全选项
|
|
135
|
+
|
|
136
|
+
`targetOrigin` 和 `allowedOrigin` 是可选的。如果省略,消息将使用 `'*'` 发送,传入的消息仅通过 `MessageEvent.source` 与预期的 iframe 或父窗口进行校验。
|
|
137
|
+
|
|
138
|
+
> **警告**:当两侧都是同一 `chrome-extension://` origin 下的 Chrome 扩展页面时,默认行为是安全的。但如果 iframe 加载的是**第三方页面**或来自**不同 origin** 的页面,不设置 `allowedOrigin` 意味着**任何窗口**都可以发送伪造的 `postMessage` 消息来冒充 Chrome API 调用。在这种情况下,务必将 `allowedOrigin` 设置为预期的 origin。
|
|
139
|
+
|
|
140
|
+
在生产环境中,如果知道具体来源,建议传入 origin。
|
|
141
|
+
|
|
142
|
+
### 扩展页面
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
exposeChromeInIframe({
|
|
146
|
+
iframe,
|
|
147
|
+
key: 'my-extension-bridge',
|
|
148
|
+
targetOrigin: 'https://your-iframe-app.example',
|
|
149
|
+
allowedOrigin: 'https://your-iframe-app.example',
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Iframe 页面
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const { chrome } = connectChromeInIframe({
|
|
157
|
+
key: 'my-extension-bridge',
|
|
158
|
+
targetOrigin: 'chrome-extension://modkelfkcfjpgbfmnbnllalkiogfofhb',
|
|
159
|
+
allowedOrigin: 'chrome-extension://modkelfkcfjpgbfmnbnllalkiogfofhb',
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 读取属性
|
|
164
|
+
|
|
165
|
+
Chrome API 方法可以直接调用:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const tabs = await chrome.tabs.query({ active: true });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
可读属性应通过 `get()` 或 `$get()` 读取:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const extensionId = await get<string>('runtime.id');
|
|
175
|
+
const platformOs = await chrome.runtime.$get<string>('PlatformOs');
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## API
|
|
179
|
+
|
|
180
|
+
### `exposeChromeInIframe(options)`
|
|
181
|
+
|
|
182
|
+
在 Chrome 扩展页面中运行。
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const bridge = exposeChromeInIframe({
|
|
186
|
+
iframe,
|
|
187
|
+
key,
|
|
188
|
+
targetOrigin,
|
|
189
|
+
allowedOrigin,
|
|
190
|
+
chromeApi,
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
选项:
|
|
195
|
+
|
|
196
|
+
- `iframe`:要桥接的 iframe 元素。
|
|
197
|
+
- `key`:可选的共享桥接密钥。两侧使用相同的 key。
|
|
198
|
+
- `targetOrigin`:可选,`postMessage` 使用的 origin。
|
|
199
|
+
- `allowedOrigin`:可选,用于过滤传入消息的 origin、origin 列表或判断函数。
|
|
200
|
+
- `chromeApi`:可选,自定义的 Chrome API 对象。默认为 `globalThis.chrome`。
|
|
201
|
+
|
|
202
|
+
返回值:
|
|
203
|
+
|
|
204
|
+
- `proxy`:桥接后的 Chrome API 代理。
|
|
205
|
+
- `get(path)`:从桥接的 Chrome API 中读取属性。
|
|
206
|
+
- `destroy()`:移除消息监听器并清除桥接状态。
|
|
207
|
+
|
|
208
|
+
### `connectChromeInIframe(options)`
|
|
209
|
+
|
|
210
|
+
在 iframe 内部运行。
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
const { chrome, get, destroy } = connectChromeInIframe({
|
|
214
|
+
key,
|
|
215
|
+
targetOrigin,
|
|
216
|
+
allowedOrigin,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
返回值:
|
|
221
|
+
|
|
222
|
+
- `chrome`:扩展页面 Chrome API 的代理。
|
|
223
|
+
- `proxy`:与 `chrome` 相同的对象。
|
|
224
|
+
- `get(path)`:从桥接的 Chrome API 中读取属性。
|
|
225
|
+
- `destroy()`:移除消息监听器并清除桥接状态。
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type AllowedOrigin = string | string[] | ((origin: string) => boolean);
|
|
2
|
+
export type PropertyPathPart = string | symbol | Array<string | symbol>;
|
|
3
|
+
export type ChromeApi = typeof globalThis extends {
|
|
4
|
+
chrome: infer Api;
|
|
5
|
+
} ? Api : unknown;
|
|
6
|
+
export interface PropertyAccess {
|
|
7
|
+
$get<R = unknown>(...path: PropertyPathPart[]): Promise<R>;
|
|
8
|
+
}
|
|
9
|
+
export interface ConnectionOptions {
|
|
10
|
+
key?: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
targetOrigin?: string;
|
|
13
|
+
allowedOrigin?: AllowedOrigin;
|
|
14
|
+
}
|
|
15
|
+
export interface SetupInMainWindowOptions<T = unknown> extends ConnectionOptions {
|
|
16
|
+
iframe: HTMLIFrameElement;
|
|
17
|
+
delegateTarget: T;
|
|
18
|
+
}
|
|
19
|
+
export type SetupInIframeOptions = ConnectionOptions;
|
|
20
|
+
export interface ExposeChromeInIframeOptions<T = ChromeApi> extends ConnectionOptions {
|
|
21
|
+
iframe: HTMLIFrameElement;
|
|
22
|
+
chromeApi?: T;
|
|
23
|
+
}
|
|
24
|
+
export interface ConnectionHandle<T> {
|
|
25
|
+
proxy: T & PropertyAccess;
|
|
26
|
+
get: PropertyAccess['$get'];
|
|
27
|
+
destroy: () => void;
|
|
28
|
+
}
|
|
29
|
+
export interface ChromeIframeHandle<T = ChromeApi> extends ConnectionHandle<T> {
|
|
30
|
+
chrome: T & PropertyAccess;
|
|
31
|
+
}
|
|
32
|
+
export declare function setupInMainWindow<T>(options: SetupInMainWindowOptions<T>): ConnectionHandle<T>;
|
|
33
|
+
export declare function setupInIframe<T>(options?: SetupInIframeOptions): ConnectionHandle<T>;
|
|
34
|
+
export declare function exposeChromeInIframe<T = ChromeApi>(options: ExposeChromeInIframeOptions<T>): ConnectionHandle<T>;
|
|
35
|
+
export declare function connectChromeInIframe<T = ChromeApi>(options?: SetupInIframeOptions): ChromeIframeHandle<T>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ProcessorRegistry } from '../processor/registry';
|
|
2
|
+
import type { ClientContext, MessageChannel, MessagePoster } from './types';
|
|
3
|
+
export declare function createMessageChannel(poster: MessagePoster, key: string, context: ClientContext, processorRegistry: ProcessorRegistry): MessageChannel;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { Callable, CallbackCacheOptions, MessageValue } from './types';
|
|
2
|
+
export declare function deserialize(arg: MessageValue, generateCallback: (id: string, args: MessageValue[], options?: CallbackCacheOptions) => MessageValue, getRemoteCallback?: (id: string, invoke: (args: MessageValue[]) => void, options?: CallbackCacheOptions) => Callable, options?: CallbackCacheOptions): MessageValue;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PathKey } from './types';
|
|
2
|
+
type SerializedSymbolPathKey = {
|
|
3
|
+
$type: 'symbol';
|
|
4
|
+
value: string;
|
|
5
|
+
};
|
|
6
|
+
export type SerializedPathKey = string | SerializedSymbolPathKey;
|
|
7
|
+
export declare function serializePath(path: PathKey[]): SerializedPathKey[];
|
|
8
|
+
export declare function deserializePath(path: SerializedPathKey[]): PathKey[];
|
|
9
|
+
export declare function isSerializedPath(value: unknown): value is SerializedPathKey[];
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { LRUCache } from 'lru-cache';
|
|
2
|
+
export type PathKey = string | symbol;
|
|
3
|
+
export type MessageValue = unknown;
|
|
4
|
+
export type Callable = (...args: MessageValue[]) => MessageValue;
|
|
5
|
+
export type RequestId = string;
|
|
6
|
+
export type SerializedError = {
|
|
7
|
+
message: string;
|
|
8
|
+
stack?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ListenerCallback = (event: {
|
|
11
|
+
data: string;
|
|
12
|
+
}) => void;
|
|
13
|
+
export interface Messages {
|
|
14
|
+
invokeRequest: {
|
|
15
|
+
id: RequestId;
|
|
16
|
+
path: PathKey[];
|
|
17
|
+
args: MessageValue[];
|
|
18
|
+
};
|
|
19
|
+
invokeResponse: {
|
|
20
|
+
id: RequestId;
|
|
21
|
+
data?: MessageValue;
|
|
22
|
+
error?: SerializedError;
|
|
23
|
+
};
|
|
24
|
+
accessPropertyRequest: {
|
|
25
|
+
id: RequestId;
|
|
26
|
+
path: PathKey[];
|
|
27
|
+
};
|
|
28
|
+
accessPropertyResponse: {
|
|
29
|
+
id: RequestId;
|
|
30
|
+
data?: MessageValue;
|
|
31
|
+
error?: SerializedError;
|
|
32
|
+
};
|
|
33
|
+
invokeFunctionByIdRequest: {
|
|
34
|
+
id: string;
|
|
35
|
+
callId: RequestId;
|
|
36
|
+
args: MessageValue[];
|
|
37
|
+
};
|
|
38
|
+
invokeFunctionByIdResponse: {
|
|
39
|
+
id: RequestId;
|
|
40
|
+
data?: MessageValue;
|
|
41
|
+
error?: SerializedError;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export type MessageType = keyof Messages;
|
|
45
|
+
export type MessageBody<K extends MessageType = MessageType> = {
|
|
46
|
+
type: K;
|
|
47
|
+
key: string;
|
|
48
|
+
data: Messages[K];
|
|
49
|
+
};
|
|
50
|
+
export interface MessagePoster {
|
|
51
|
+
postMessage(message: string): void;
|
|
52
|
+
addEventListener(name: 'message', callback: ListenerCallback): void;
|
|
53
|
+
removeEventListener(name: 'message', callback: ListenerCallback): void;
|
|
54
|
+
}
|
|
55
|
+
export interface PromiseCallbacks {
|
|
56
|
+
resolve: (value: MessageValue) => void;
|
|
57
|
+
reject: (reason: MessageValue) => void;
|
|
58
|
+
timer: ReturnType<typeof setTimeout>;
|
|
59
|
+
}
|
|
60
|
+
export interface ClientContext {
|
|
61
|
+
getDelegateTarget(): MessageValue;
|
|
62
|
+
getMessageChannel(): MessageChannel;
|
|
63
|
+
getFunctionCache(): LRUCache<string, Callable>;
|
|
64
|
+
getPersistentFunctionCache(): Map<string, Callable>;
|
|
65
|
+
registerFunction(fn: Callable, options?: CallbackCacheOptions): string;
|
|
66
|
+
getRemoteCallback(id: string, invoke: (args: MessageValue[]) => MessageValue, options?: CallbackCacheOptions): Callable;
|
|
67
|
+
getAndRemovePendingPromise(id: RequestId): PromiseCallbacks | undefined;
|
|
68
|
+
invoke(path: PathKey[], args: MessageValue[]): Promise<MessageValue>;
|
|
69
|
+
accessProperty(path: PathKey[]): Promise<MessageValue>;
|
|
70
|
+
invokeFunctionById(id: string, args: MessageValue[], options?: CallbackCacheOptions): Promise<MessageValue>;
|
|
71
|
+
registerPendingPromise(id: RequestId, callbacks: PromiseCallbacks): void;
|
|
72
|
+
destroy(): void;
|
|
73
|
+
}
|
|
74
|
+
export interface CallbackCacheOptions {
|
|
75
|
+
persistent?: boolean;
|
|
76
|
+
}
|
|
77
|
+
export interface MessageChannel {
|
|
78
|
+
getSender(): MessageSender;
|
|
79
|
+
getContext(): ClientContext;
|
|
80
|
+
getPoster(): MessagePoster;
|
|
81
|
+
getKey(): string;
|
|
82
|
+
destroy(): void;
|
|
83
|
+
}
|
|
84
|
+
export interface MessageSender {
|
|
85
|
+
sendMessage<K extends MessageType>(type: K, data: Messages[K]): void;
|
|
86
|
+
}
|
|
87
|
+
export interface RequestHandler<K extends MessageType = MessageType> {
|
|
88
|
+
(data: Messages[K], ctx: ClientContext): void;
|
|
89
|
+
}
|
|
90
|
+
export type ProxyNode = {
|
|
91
|
+
parent?: ProxyNode;
|
|
92
|
+
key: PathKey;
|
|
93
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SerializedError } from './types';
|
|
2
|
+
export declare const WELL_KNOWN_SYMBOLS: Record<string, symbol>;
|
|
3
|
+
export declare function getWellKnownSymbolName(value: symbol): string | undefined;
|
|
4
|
+
export declare function getWellKnownSymbol(name: string): symbol | undefined;
|
|
5
|
+
export declare function serializeThrownError(err: unknown): SerializedError;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ClientContext, MessageChannel, MessageValue } from '../channel/types';
|
|
2
|
+
export interface ClientContextOptions {
|
|
3
|
+
delegateTarget?: MessageValue;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function createClientContext(getMessageChannel: () => MessageChannel, options?: ClientContextOptions): ClientContext;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MessagePoster } from '../channel/types';
|
|
2
|
+
import { type ClientContextOptions } from './context';
|
|
3
|
+
export interface EndpointOptions {
|
|
4
|
+
poster: MessagePoster;
|
|
5
|
+
key?: string;
|
|
6
|
+
contextOptions?: ClientContextOptions;
|
|
7
|
+
}
|
|
8
|
+
export interface Endpoint<TProxy = unknown> {
|
|
9
|
+
proxy: TProxy;
|
|
10
|
+
destroy: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function createEndpoint<TProxy = unknown>(options: EndpointOptions): Endpoint<TProxy>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MessageValue, PathKey, ProxyNode } from '../channel/types';
|
|
2
|
+
export declare function resolvePath(node: ProxyNode): PathKey[];
|
|
3
|
+
export declare function createClientProxy<TProxy = unknown>(invoke: (path: PathKey[], args: MessageValue[]) => Promise<MessageValue>, accessProperty?: (path: PathKey[]) => Promise<MessageValue>): TProxy;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { connectChromeInIframe, exposeChromeInIframe, setupInIframe, setupInMainWindow } from './api';
|
|
2
|
+
export type { AllowedOrigin, PropertyAccess, PropertyPathPart, ConnectionHandle, ChromeIframeHandle, ExposeChromeInIframeOptions, SetupInIframeOptions, SetupInMainWindowOptions, ConnectionOptions, } from './api';
|
|
3
|
+
export type { Messages, MessageType, MessageBody, MessagePoster, ClientContext, MessageChannel, MessageSender, ProxyNode, ListenerCallback, SerializedError, } from './channel/types';
|