js-rpc2 2.2.0 → 2.3.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 +52 -0
- package/doc/chrome-extensions.md +133 -0
- package/doc/electron.md +138 -0
- package/package.json +5 -2
- package/src/lib.js +117 -1
- package/src/types.ts +45 -2
package/README.md
CHANGED
@@ -355,4 +355,56 @@ async function postPortMessage() {
|
|
355
355
|
let ret = await rpc.hello('6667');
|
356
356
|
console.info("ret from rpc:", ret);
|
357
357
|
}
|
358
|
+
```
|
359
|
+
|
360
|
+
## chrome extensions
|
361
|
+
```js
|
362
|
+
import {createRpcServerChromeExtensions, createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
363
|
+
|
364
|
+
// background.js
|
365
|
+
export class RpcBackgroundApi {
|
366
|
+
/**
|
367
|
+
* @param {(progress: string, date: Date) => Promise<void>} cb
|
368
|
+
*/
|
369
|
+
async callback(cb) {
|
370
|
+
console.info('callback in background', 'cb')
|
371
|
+
for (let i = 0; i < 10; i++) {
|
372
|
+
await sleep(1000)
|
373
|
+
await cb(`from background index is ${i}`, new Date())
|
374
|
+
}
|
375
|
+
return 'over!'
|
376
|
+
}
|
377
|
+
}
|
378
|
+
createRpcServerChromeExtensions({ chrome, key: 'rpc-popup->background', extension: new RpcBackgroundApi(), })
|
379
|
+
createRpcServerChromeExtensions({ chrome, key: 'rpc-content-scripts->background', extension: new RpcBackgroundApi(), })
|
380
|
+
|
381
|
+
|
382
|
+
// content_scripts.js
|
383
|
+
export class RpcContentScriptsApi {
|
384
|
+
async callBackgroundWithCallback(name, callback) {
|
385
|
+
console.info('name', name)
|
386
|
+
return await rpcContentScriptBackground.callback(callback)
|
387
|
+
}
|
388
|
+
}
|
389
|
+
createRpcServerChromeExtensions({ chrome, key: 'rpc-popup->content-script', extension: new RpcContentScriptsApi() })
|
390
|
+
/** @type{RpcBackgroundApi} */
|
391
|
+
export const rpcContentScriptBackground = createRpcClientChromeExtensions({ chrome, key: 'rpc-content-scripts->background' })
|
392
|
+
|
393
|
+
|
394
|
+
// popup.js
|
395
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
396
|
+
/** @type{RpcContentScriptsApi} */
|
397
|
+
export const rpcPopupContentScripts = createRpcClientChromeExtensions({ chrome, key: 'rpc-popup->content-script', tabId: tab.id, })
|
398
|
+
/** @type{RpcBackgroundApi} */
|
399
|
+
export const rpcPopupBackground = createRpcClientChromeExtensions({ chrome, key: 'rpc-popup->background' })
|
400
|
+
|
401
|
+
let resp = await rpcPopupContentScripts.callBackgroundWithCallback("name", async (progress, date) => {
|
402
|
+
console.info('at popup callback ',progress, date)
|
403
|
+
})
|
404
|
+
console.info("over!", resp)
|
405
|
+
|
406
|
+
let resp = await rpcPopupBackground.callback(async (progress, date) => {
|
407
|
+
console.info('at popup callback ',progress, date)
|
408
|
+
})
|
409
|
+
console.info("over!", resp)
|
358
410
|
```
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# Chrome Extensions RPC 通信使用说明
|
2
|
+
|
3
|
+
## 概述
|
4
|
+
|
5
|
+
本项目使用 `js-rpc2` 库实现在 Chrome 扩展各组件间的远程过程调用(RPC)通信,支持在 popup、content scripts 和 background 之间进行双向通信。
|
6
|
+
|
7
|
+
## 项目结构
|
8
|
+
|
9
|
+
### 组件说明
|
10
|
+
|
11
|
+
- **background.js**: 后台脚本,提供核心服务接口
|
12
|
+
- **content_scripts.js**: 内容脚本,运行在网页上下文中,可作为通信中继
|
13
|
+
- **popup.js**: 扩展弹窗界面,用户交互入口
|
14
|
+
|
15
|
+
## 核心实现
|
16
|
+
|
17
|
+
### Background 脚本
|
18
|
+
|
19
|
+
```javascript
|
20
|
+
import {createRpcServerChromeExtensions} from 'js-rpc2/src/lib.js'
|
21
|
+
|
22
|
+
export class RpcBackgroundApi {
|
23
|
+
/**
|
24
|
+
* 带回调的示例方法
|
25
|
+
* @param {(progress: string, date: Date) => Promise<void>} cb - 回调函数
|
26
|
+
*/
|
27
|
+
async callback(cb) {
|
28
|
+
for (let i = 0; i < 10; i++) {
|
29
|
+
await sleep(1000)
|
30
|
+
await cb(`from background index is ${i}`, new Date())
|
31
|
+
}
|
32
|
+
return 'over!'
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
// 创建RPC服务端点
|
37
|
+
createRpcServerChromeExtensions({
|
38
|
+
chrome,
|
39
|
+
key: 'rpc-popup->background',
|
40
|
+
extension: new RpcBackgroundApi()
|
41
|
+
})
|
42
|
+
|
43
|
+
createRpcServerChromeExtensions({
|
44
|
+
chrome,
|
45
|
+
key: 'rpc-content-scripts->background',
|
46
|
+
extension: new RpcBackgroundApi()
|
47
|
+
})
|
48
|
+
```
|
49
|
+
|
50
|
+
### Content Scripts 脚本
|
51
|
+
|
52
|
+
```javascript
|
53
|
+
import {createRpcServerChromeExtensions, createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
54
|
+
|
55
|
+
export class RpcContentScriptsApi {
|
56
|
+
async callBackgroundWithCallback(name, callback) {
|
57
|
+
return await rpcContentScriptBackground.callback(callback)
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
// 创建RPC服务端点供popup调用
|
62
|
+
createRpcServerChromeExtensions({
|
63
|
+
chrome,
|
64
|
+
key: 'rpc-popup->content-script',
|
65
|
+
extension: new RpcContentScriptsApi()
|
66
|
+
})
|
67
|
+
|
68
|
+
// 创建RPC客户端连接到background
|
69
|
+
export const rpcContentScriptBackground = createRpcClientChromeExtensions({
|
70
|
+
chrome,
|
71
|
+
key: 'rpc-content-scripts->background'
|
72
|
+
})
|
73
|
+
```
|
74
|
+
|
75
|
+
### Popup 脚本
|
76
|
+
|
77
|
+
```javascript
|
78
|
+
import {createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
79
|
+
|
80
|
+
// 获取当前活动标签页
|
81
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
82
|
+
|
83
|
+
// 创建RPC客户端
|
84
|
+
export const rpcPopupContentScripts = createRpcClientChromeExtensions({
|
85
|
+
chrome,
|
86
|
+
key: 'rpc-popup->content-script',
|
87
|
+
tabId: tab.id
|
88
|
+
})
|
89
|
+
|
90
|
+
export const rpcPopupBackground = createRpcClientChromeExtensions({
|
91
|
+
chrome,
|
92
|
+
key: 'rpc-popup->background'
|
93
|
+
})
|
94
|
+
|
95
|
+
// 使用示例
|
96
|
+
let resp = await rpcPopupContentScripts.callBackgroundWithCallback("name", async (progress, date) => {
|
97
|
+
console.info('at popup callback', progress, date)
|
98
|
+
})
|
99
|
+
console.info("over!", resp)
|
100
|
+
|
101
|
+
let resp2 = await rpcPopupBackground.callback(async (progress, date) => {
|
102
|
+
console.info('at popup callback', progress, date)
|
103
|
+
})
|
104
|
+
console.info("over!", resp2)
|
105
|
+
```
|
106
|
+
|
107
|
+
## 通信模式
|
108
|
+
|
109
|
+
### 直接通信
|
110
|
+
Popup → Background
|
111
|
+
```javascript
|
112
|
+
rpcPopupBackground.callback(callbackFunction)
|
113
|
+
```
|
114
|
+
|
115
|
+
### 间接通信
|
116
|
+
Popup → Content Script → Background
|
117
|
+
```javascript
|
118
|
+
rpcPopupContentScripts.callBackgroundWithCallback(name, callbackFunction)
|
119
|
+
```
|
120
|
+
|
121
|
+
## 使用方法
|
122
|
+
|
123
|
+
1. 在各组件中导入相应的 RPC 创建函数
|
124
|
+
2. 定义服务接口类
|
125
|
+
3. 使用 `createRpcServerChromeExtensions` 创建服务端点
|
126
|
+
4. 使用 `createRpcClientChromeExtensions` 创建客户端连接
|
127
|
+
5. 通过客户端实例调用远程方法
|
128
|
+
|
129
|
+
## 注意事项
|
130
|
+
|
131
|
+
- 确保 manifest.json 中正确配置了各组件的权限和通信策略
|
132
|
+
- 注意处理异步回调的生命周期管理
|
133
|
+
- 建议为所有 RPC 接口添加适当的错误处理机制
|
package/doc/electron.md
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# Electron RPC 通信使用说明
|
2
|
+
|
3
|
+
## 概述
|
4
|
+
|
5
|
+
本文档介绍如何在 Electron 应用中使用 `js-rpc2` 库实现主进程与渲染进程之间的 RPC 通信。通过 MessagePort 机制,实现跨进程的远程方法调用。
|
6
|
+
|
7
|
+
## 项目结构
|
8
|
+
|
9
|
+
### 组件说明
|
10
|
+
|
11
|
+
- **main.js**: Electron 主进程,提供核心服务接口
|
12
|
+
- **preload.js**: 预加载脚本,建立主进程与渲染进程的通信桥梁
|
13
|
+
- **renderer.js**: 渲染进程,运行在浏览器环境中
|
14
|
+
- **usage.js**: 使用示例,展示如何调用 RPC 方法
|
15
|
+
|
16
|
+
## 核心实现
|
17
|
+
|
18
|
+
### 主进程 (main.js)
|
19
|
+
|
20
|
+
```javascript
|
21
|
+
import { ipcMain } from 'electron'
|
22
|
+
import { createRpcServerElectronMessagePort } from 'js-rpc2/src/lib.js'
|
23
|
+
|
24
|
+
class AppApi {
|
25
|
+
asyncLocalStorage = new AsyncLocalStorage()
|
26
|
+
|
27
|
+
async hello(param) {
|
28
|
+
return 'wertyuioiuytre ' + param
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
// 监听来自渲染进程的 port 消息
|
33
|
+
ipcMain.on('port', (event) => {
|
34
|
+
const port = event.ports[0]
|
35
|
+
// 创建 RPC 服务端点
|
36
|
+
createRpcServerElectronMessagePort({
|
37
|
+
port,
|
38
|
+
rpcKey: '',
|
39
|
+
extension: new AppApi()
|
40
|
+
})
|
41
|
+
port.start()
|
42
|
+
})
|
43
|
+
```
|
44
|
+
|
45
|
+
### 预加载脚本 (preload.js)
|
46
|
+
|
47
|
+
```javascript
|
48
|
+
import { ipcRenderer } from 'electron/renderer'
|
49
|
+
|
50
|
+
// 监听来自渲染进程的消息
|
51
|
+
window.onmessage = (/** @type {MessageEvent} */ event) => {
|
52
|
+
// 验证消息来源并转发给主进程
|
53
|
+
if (event.origin == location.origin && event.data != 'port') {
|
54
|
+
ipcRenderer.postMessage('port', null, [event.ports[0]])
|
55
|
+
}
|
56
|
+
}
|
57
|
+
```
|
58
|
+
|
59
|
+
### 渲染进程 (renderer.js)
|
60
|
+
|
61
|
+
```javascript
|
62
|
+
import { createRpcClientMessagePort } from 'js-rpc2/src/lib.js'
|
63
|
+
|
64
|
+
// 创建消息通道
|
65
|
+
const channel = new MessageChannel();
|
66
|
+
|
67
|
+
// 向主进程发送端口
|
68
|
+
window.postMessage("port", location.origin, [channel.port1]);
|
69
|
+
|
70
|
+
// 创建 RPC 客户端
|
71
|
+
let rpc = createRpcClientMessagePort({
|
72
|
+
port: channel.port2,
|
73
|
+
rpcKey: ""
|
74
|
+
});
|
75
|
+
```
|
76
|
+
|
77
|
+
### 使用示例 (usage.js)
|
78
|
+
|
79
|
+
```javascript
|
80
|
+
async function postPortMessage() {
|
81
|
+
// 调用远程方法
|
82
|
+
let ret = await rpc.hello('6667');
|
83
|
+
console.info("ret from rpc:", ret);
|
84
|
+
}
|
85
|
+
```
|
86
|
+
|
87
|
+
## 通信流程
|
88
|
+
|
89
|
+
1. 渲染进程创建 `MessageChannel` 并通过 `postMessage` 将 `port1` 发送到预加载脚本
|
90
|
+
2. 预加载脚本验证消息来源并将端口转发给主进程
|
91
|
+
3. 主进程接收端口并创建 RPC 服务端点
|
92
|
+
4. 渲染进程使用 `port2` 创建 RPC 客户端
|
93
|
+
5. 客户端可以调用主进程中定义的服务方法
|
94
|
+
|
95
|
+
## 使用方法
|
96
|
+
|
97
|
+
### 1. 主进程设置
|
98
|
+
|
99
|
+
在主进程中定义服务类并创建 RPC 服务:
|
100
|
+
|
101
|
+
```javascript
|
102
|
+
class AppApi {
|
103
|
+
async yourMethod(params) {
|
104
|
+
// 实现业务逻辑
|
105
|
+
return result;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
ipcMain.on('port', (event) => {
|
110
|
+
const port = event.ports[0]
|
111
|
+
createRpcServerElectronMessagePort({
|
112
|
+
port,
|
113
|
+
rpcKey: '',
|
114
|
+
extension: new AppApi()
|
115
|
+
})
|
116
|
+
port.start()
|
117
|
+
})
|
118
|
+
```
|
119
|
+
|
120
|
+
### 2. 渲染进程调用
|
121
|
+
|
122
|
+
在渲染进程中建立连接并调用远程方法:
|
123
|
+
|
124
|
+
```javascript
|
125
|
+
const channel = new MessageChannel();
|
126
|
+
window.postMessage("port", location.origin, [channel.port1]);
|
127
|
+
let rpc = createRpcClientMessagePort({ port: channel.port2, rpcKey: "" });
|
128
|
+
|
129
|
+
// 调用远程方法
|
130
|
+
let result = await rpc.yourMethod(params);
|
131
|
+
```
|
132
|
+
|
133
|
+
## 注意事项
|
134
|
+
|
135
|
+
- 确保在 Electron 的安全策略下正确配置 `contextIsolation` 和 `sandbox` 选项
|
136
|
+
- 预加载脚本中的消息验证是安全通信的关键
|
137
|
+
- MessagePort 需要在两端都启动后才能正常通信
|
138
|
+
- 异步方法调用需要正确处理 Promise 返回值
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-rpc2",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.3.0",
|
4
4
|
"description": "js web websocket http rpc",
|
5
5
|
"main": "index.js",
|
6
6
|
"type": "module",
|
@@ -21,7 +21,10 @@
|
|
21
21
|
"web",
|
22
22
|
"http",
|
23
23
|
"websocket",
|
24
|
-
"rpc"
|
24
|
+
"rpc",
|
25
|
+
"electron",
|
26
|
+
"chrome",
|
27
|
+
"extensions"
|
25
28
|
],
|
26
29
|
"author": "yuanliwei",
|
27
30
|
"license": "MIT",
|
package/src/lib.js
CHANGED
@@ -518,7 +518,7 @@ export function createRpcServerHelper(param) {
|
|
518
518
|
decode.readable.pipeTo(new WritableStream({
|
519
519
|
async write(buffer) {
|
520
520
|
let asyncLocalStorage = param.extension.asyncLocalStorage
|
521
|
-
asyncLocalStorage
|
521
|
+
asyncLocalStorage?.enterWith(param.context)
|
522
522
|
if (param.async) {
|
523
523
|
rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger).catch(console.error)
|
524
524
|
} else {
|
@@ -832,3 +832,119 @@ export function createRpcClientMessagePort(param) {
|
|
832
832
|
}
|
833
833
|
return createRPCProxy(helper.apiInvoke)
|
834
834
|
}
|
835
|
+
|
836
|
+
/**
|
837
|
+
* @param {string} text
|
838
|
+
* @returns
|
839
|
+
*/
|
840
|
+
export function base64decode(text) {
|
841
|
+
const _tidyB64 = (/** @type {string} */ s) => s.replace(/[^A-Za-z0-9\+\/]/g, '')
|
842
|
+
const _unURI = (/** @type {string} */ a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/'))
|
843
|
+
text = _unURI(text)
|
844
|
+
return new Uint8Array(globalThis.atob(text).split('').map(c => c.charCodeAt(0)))
|
845
|
+
}
|
846
|
+
|
847
|
+
/**
|
848
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
849
|
+
* @returns
|
850
|
+
*/
|
851
|
+
export function base64encode(buffer, urlsafe = false) {
|
852
|
+
let b64 = globalThis.btoa(String.fromCharCode(...new Uint8Array(buffer)))
|
853
|
+
if (urlsafe) {
|
854
|
+
b64 = b64.replace(/=/g, '').replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_')
|
855
|
+
}
|
856
|
+
return b64
|
857
|
+
}
|
858
|
+
|
859
|
+
/**
|
860
|
+
* @import {chrome as Chrome} from './types.js'
|
861
|
+
* @template T
|
862
|
+
* @param {{
|
863
|
+
* chrome:Chrome;
|
864
|
+
* key: string;
|
865
|
+
* extension: ExtensionApi<T>
|
866
|
+
* logger?:(msg:string)=>void;
|
867
|
+
* }} param
|
868
|
+
*/
|
869
|
+
export function createRpcServerChromeExtensions(param) {
|
870
|
+
const chrome = param.chrome
|
871
|
+
const actionMap = new Map()
|
872
|
+
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
|
873
|
+
if (request.action === param.key) {
|
874
|
+
let tabId = sender.tab?.id
|
875
|
+
let { keyServer, keyClient } = request.data
|
876
|
+
let helper = createRpcServerHelper({
|
877
|
+
rpcKey: '', extension: param.extension, async: true, logger: param.logger,
|
878
|
+
})
|
879
|
+
let writer = helper.writable.getWriter()
|
880
|
+
actionMap.set(keyServer, (/** @type {Uint8Array<ArrayBuffer>} */ data) => {
|
881
|
+
writer.write(data)
|
882
|
+
})
|
883
|
+
helper.readable.pipeTo(new WritableStream({
|
884
|
+
async write(chunk) {
|
885
|
+
let resp = null
|
886
|
+
if (tabId) {
|
887
|
+
resp = await chrome.tabs.sendMessage(tabId, { action: keyClient, data: base64encode(chunk), })
|
888
|
+
} else {
|
889
|
+
resp = await chrome.runtime.sendMessage({ action: keyClient, data: base64encode(chunk), })
|
890
|
+
}
|
891
|
+
if (!resp) {
|
892
|
+
actionMap.delete(keyServer)
|
893
|
+
}
|
894
|
+
}
|
895
|
+
}))
|
896
|
+
sendResponse(true)
|
897
|
+
}
|
898
|
+
let write = actionMap.get(request.action)
|
899
|
+
if (write) {
|
900
|
+
write(base64decode(request.data))
|
901
|
+
sendResponse(true)
|
902
|
+
}
|
903
|
+
})
|
904
|
+
}
|
905
|
+
|
906
|
+
/**
|
907
|
+
* @param {{
|
908
|
+
* chrome:Chrome;
|
909
|
+
* key:string;
|
910
|
+
* tabId?: number;
|
911
|
+
* }} param
|
912
|
+
*/
|
913
|
+
export function createRpcClientChromeExtensions(param) {
|
914
|
+
const chrome = param.chrome
|
915
|
+
let helper = createRpcClientHelper({ rpcKey: '' })
|
916
|
+
let writer = helper.writable.getWriter()
|
917
|
+
!(async () => {
|
918
|
+
let keyServer = guid()
|
919
|
+
let keyClient = guid()
|
920
|
+
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
|
921
|
+
if (request.action === keyClient) {
|
922
|
+
sendResponse(true)
|
923
|
+
await writer.write(base64decode(request.data))
|
924
|
+
}
|
925
|
+
})
|
926
|
+
helper.readable.pipeTo(new WritableStream({
|
927
|
+
async write(chunk) {
|
928
|
+
if (param.tabId) {
|
929
|
+
await chrome.tabs.sendMessage(param.tabId, { action: keyServer, data: base64encode(chunk) })
|
930
|
+
} else {
|
931
|
+
await chrome.runtime.sendMessage({ action: keyServer, data: base64encode(chunk) })
|
932
|
+
}
|
933
|
+
}
|
934
|
+
}))
|
935
|
+
let response = null
|
936
|
+
try {
|
937
|
+
if (param.tabId) {
|
938
|
+
response = await chrome.tabs.sendMessage(param.tabId, { action: param.key, data: { keyServer, keyClient }, })
|
939
|
+
} else {
|
940
|
+
response = await chrome.runtime.sendMessage({ action: param.key, data: { keyServer, keyClient }, })
|
941
|
+
}
|
942
|
+
} catch (error) {
|
943
|
+
throw new Error(`Failed to establish RPC connection: ${error.message}`)
|
944
|
+
}
|
945
|
+
if (!response) {
|
946
|
+
throw new Error('RPC server did not respond - connection failed')
|
947
|
+
}
|
948
|
+
})()
|
949
|
+
return createRPCProxy(helper.apiInvoke)
|
950
|
+
}
|
package/src/types.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
2
2
|
|
3
|
-
export type ExtensionApi<T> = { asyncLocalStorage
|
3
|
+
export type ExtensionApi<T> = { asyncLocalStorage?: AsyncLocalStorage<T> } & object;
|
4
4
|
|
5
5
|
|
6
6
|
export type RPC_TYPE_CALL = 0xdf68f4cb
|
@@ -52,6 +52,11 @@ export declare namespace Electron {
|
|
52
52
|
|
53
53
|
const NodeEventEmitter: typeof import('events').EventEmitter;
|
54
54
|
|
55
|
+
interface MessageEvent {
|
56
|
+
data: any;
|
57
|
+
ports: MessagePortMain[];
|
58
|
+
}
|
59
|
+
|
55
60
|
class MessagePortMain extends NodeEventEmitter {
|
56
61
|
|
57
62
|
// Docs: https://electronjs.org/docs/api/message-port-main
|
@@ -88,4 +93,42 @@ export declare namespace Electron {
|
|
88
93
|
start(): void;
|
89
94
|
}
|
90
95
|
|
91
|
-
}
|
96
|
+
}
|
97
|
+
|
98
|
+
export declare namespace chrome {
|
99
|
+
namespace events {
|
100
|
+
interface Event<T extends (...args: any) => void> {
|
101
|
+
addListener(callback: T): void;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
namespace tabs {
|
105
|
+
interface Tab {
|
106
|
+
id?: number | undefined;
|
107
|
+
}
|
108
|
+
interface MessageSender {
|
109
|
+
tab?: chrome.tabs.Tab;
|
110
|
+
}
|
111
|
+
export interface MessageSendOptions {
|
112
|
+
}
|
113
|
+
export function sendMessage<M = any, R = any>(
|
114
|
+
tabId: number,
|
115
|
+
message: M,
|
116
|
+
options?: MessageSendOptions,
|
117
|
+
): Promise<R>;
|
118
|
+
interface MessageOptions {
|
119
|
+
includeTlsChannelId?: boolean | undefined;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
namespace runtime {
|
123
|
+
interface MessageSender {
|
124
|
+
tab?: chrome.tabs.Tab;
|
125
|
+
}
|
126
|
+
const onMessage: events.Event<
|
127
|
+
(message: any, sender: MessageSender, sendResponse: (response?: any) => void) => void
|
128
|
+
>;
|
129
|
+
function sendMessage<M = any, R = any>(message: M, options?: MessageOptions): Promise<R>;
|
130
|
+
interface MessageOptions {
|
131
|
+
includeTlsChannelId?: boolean | undefined;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|