Skip to content
This repository has been archived by the owner on Apr 7, 2024. It is now read-only.

Commit

Permalink
feat: ts type && invoke args
Browse files Browse the repository at this point in the history
  • Loading branch information
kinglisky committed Apr 1, 2022
1 parent 8ee7330 commit ba76274
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 73 deletions.
69 changes: 46 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,19 +407,31 @@ removeMethod(method: string);
```ts
invoke(
method: string,
params: any,
options: RPCInvokeOptions = { isNotify: false, timeout: 0 }
...args: any[] | [...any[], RPCInvokeOptions]
): Promise<any>;
```

```ts
rpc1.registerMethod('add', (a: number, b: number, c: number) => {
return new Promise((resolve) => {
setTimeout(() => resolve(a + b + c), 400);
});
});
// 可以通过 invokeOptions 配置调用超时
const res1 = await rpc2.invoke('add', 1, 2, 3);
const res2 = await rpc2.invoke('add', 4, 5, 6, { isNotify: true });
const res3 = await rpc2.invoke('add', 7, 8, 9, { timeout: 1000 });
const res4 = await rpc2.invoke('add', 10, 11, 12, { timeout: 4 }).catch((error) => error);
```

调用远程服务

- method `string` 方法名
- params `any` 参数
- args `any` 参数
- invokeOptions.timeout `number` timeout 超时设置,会覆盖全局设置
- invokeOptions.isNotify `boolean` 是否是个一个通知消息

如果 invoke 配置了 isNotify,则作为一个通知消息,方法调用后会立即返回,不理会目标服务是否相应,目标也不会响应回复此消息。内部使用 JSON-PRC 的 id 进行标识。
可以函数调用参数末尾配置 invokeOptions 选项,如果 invoke 配置了 isNotify,则作为一个通知消息,方法调用后会立即返回,不理会目标服务是否相应,目标也不会响应回复此消息。内部使用 JSON-PRC 的 id 进行标识。

> 没有包含“id”成员的请求对象为通知, 作为通知的请求对象表明客户端对相应的响应对象并不感兴趣,本身也没有响应对象需要返回给客户端。服务端必须不回复一个通知,包含那些批量请求中的。
> 由于通知没有返回的响应对象,所以通知不确定是否被定义。同样,客户端不会意识到任何错误(例如参数缺省,内部错误)。
Expand Down Expand Up @@ -503,14 +515,14 @@ interface RPCPostMessageConfig {
}

interface RPCMessageEventOptions {
currentEndpoint: Window | Worker | MessagePort;
targetEndpoint: Window | Worker | MessagePort;
currentEndpoint: RPCMessageReceiveEndpoint;
targetEndpoint: RPCMessageSendEndpoint;
config?:
| ((data: any, context: Window | Worker | MessagePort) => RPCPostMessageConfig)
| ((data: any, context: RPCMessageSendEndpoint) => RPCPostMessageConfig)
| RPCPostMessageConfig;
sendAdapter?: (
data: RPCMessageDataFormat,
context: Window | Worker | MessagePort
data: RPCMessageDataFormat | any,
context: RPCMessageSendEndpoint
) => {
data: RPCMessageDataFormat;
transfer?: Transferable[];
Expand All @@ -519,24 +531,35 @@ interface RPCMessageEventOptions {
}
```

| 参数 | 类型 | 说明 |
| :-------------- | :---------------------------------------- | :---------------------------------------------------------------------- |
| currentEndpoint | 必填 `Window``Worker``MessagePort` | 当前通信对象的上下文,可以是 `Window``Worker` 或者 `MessagePort` 对象 |
| targetEndpoint | 必填 `Window``Worker``MessagePort` | 目标通信对象的上下文,可以是 `Window``Worker` 或者 `MessagePort` 对象 |
| config | 可选 `RPCPostMessageConfig` or `Function` | 用于给 targetEndpoint.postMessage 方法配置参数 |
| sendAdapter | 可选 `Function` | 消息发动前数据处理函数 |
| receiveAdapter | 可选 `Function` | 消息接受前数据处理函数 |
| 参数 | 类型 | 说明 |
| :-------------- | :----------------------------------------------------------------------------------- | :---------------------------------------------------------------------- |
| currentEndpoint | 必填 `Window``Worker``MessagePort` 等满足 [RPCMessageReceiveEndpoint]() 接口对象 | 当前通信对象的上下文,可以是 `Window``Worker` 或者 `MessagePort` 对象 |
| targetEndpoint | 必填 `Window``Worker``MessagePort` 等满足 [RPCMessageSendEndpoint]() 接口对象 | 目标通信对象的上下文,可以是 `Window``Worker` 或者 `MessagePort` 对象 |
| config | 可选 `RPCPostMessageConfig` or `Function` | 用于给 targetEndpoint.postMessage 方法配置参数 |
| sendAdapter | 可选 `Function` | 消息发动前数据处理函数 |
| receiveAdapter | 可选 `Function` | 消息接受前数据处理函数 |

**config**

```ts
type config =
| ((data: any, context: Window | Worker | MessagePort) => RPCPostMessageConfig)
| RPCPostMessageConfig;
interface WindowPostMessageOptions {
transfer?: Transferable[];
targetOrigin?: string;
}

interface RPCPostMessageConfig extends WindowPostMessageOptions {
}

type config?:
| ((data: any, context: RPCMessageSendEndpoint) => RPCPostMessageConfig)
| RPCPostMessageConfig;
```

用于给 targetEndpoint 的 `postMessage` 方法配置参数,可以直接配置一个对象,也可以通过函数动态返回一个配置。
目前只有针对 `window.postMessage``targetOrigin` 配置。

-`window.postMessage` 配置 `targetOrigin` -> `{ targetOrigin: '*' }`
-`worker.postMessage` 配置 `transfer` -> `{ transfer: [...] }`
-`figma.ui.postMessage` 配置 `origin` -> `{ origin: '*' }`

```ts
new RPCMessageEvent({
Expand All @@ -553,8 +576,8 @@ new RPCMessageEvent({

```ts
type sendAdapter = (
data: RPCMessageDataFormat,
context: Window | Worker | MessagePort
data: RPCMessageDataFormat | any,
context: RPCMessageSendEndpoint
) => {
data: RPCMessageDataFormat;
transfer?: Transferable[];
Expand Down Expand Up @@ -681,7 +704,7 @@ yarn build

# TODO

- [-] 添加测试用例
- [ ] 添加测试用例
- [ ] onmessage 需要检查消息来源
- [ ] proxy 化
- [ ] 协程支持
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "rpc-shooter",
"private": false,
"version": "0.0.13",
"version": "0.0.14",
"files": [
"lib"
],
Expand Down
59 changes: 26 additions & 33 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,25 @@ export interface RPCPostMessageConfig extends WindowPostMessageOptions {}

export interface AbstractMessageSendEndpoint {
// BroadcastChannel
postMessage(message: any): void;
// postMessage(message: any): void;
// Wroker && ServiceWorker && MessagePort
postMessage(message: any, transfer: Transferable[]): void;
postMessage(message: any, options?: StructuredSerializeOptions): void;
// postMessage(message: any, transfer: Transferable[]): void;
// postMessage(message: any, options?: StructuredSerializeOptions): void;
// window
// postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void;
postMessage(message: any, options?: WindowPostMessageOptions): void;
postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void;
}
export interface AbstractMessageReceiveEndpoint extends EventTarget, AbstractMessageSendEndpoint {
onmessage?: ((this: AbstractMessageReceiveEndpoint, ev: MessageEvent) => any) | null;
onmessageerror?: ((this: AbstractMessageReceiveEndpoint, ev: MessageEvent) => any) | null;
export interface AbstractMessageReceiveEndpoint extends AbstractMessageSendEndpoint {
onmessage?: ((this: any, ev: MessageEvent) => any) | null;
onmessageerror?: ((this: any, ev: MessageEvent) => any) | null;
/** Disconnects the port, so that it is no longer active. */
close?: () => void;
/** Begins dispatching messages received on the port. */
start?: () => void;

addEventListener<K extends keyof MessagePortEventMap>(
type: K,
listener: (this: AbstractMessageReceiveEndpoint, ev: MessagePortEventMap[K]) => any,
listener: (this: any, ev: MessagePortEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
): void;
addEventListener(
Expand All @@ -53,7 +53,7 @@ export interface AbstractMessageReceiveEndpoint extends EventTarget, AbstractMes
): void;
removeEventListener<K extends keyof MessagePortEventMap>(
type: K,
listener: (this: AbstractMessageReceiveEndpoint, ev: MessagePortEventMap[K]) => any,
listener: (this: any, ev: MessagePortEventMap[K]) => any,
options?: boolean | EventListenerOptions
): void;
removeEventListener(
Expand All @@ -63,21 +63,9 @@ export interface AbstractMessageReceiveEndpoint extends EventTarget, AbstractMes
): void;
}

export type RPCMessageReceiveEndpoint =
| Window
| Worker
| ServiceWorker
| MessagePort
| BroadcastChannel
| AbstractMessageReceiveEndpoint;

export type RPCMessageSendEndpoint =
| Window
| Worker
| ServiceWorker
| MessagePort
| BroadcastChannel
| AbstractMessageSendEndpoint;
export interface RPCMessageReceiveEndpoint extends AbstractMessageReceiveEndpoint {}

export interface RPCMessageSendEndpoint extends AbstractMessageSendEndpoint {}

export interface RPCMessageEventOptions {
currentEndpoint: RPCMessageReceiveEndpoint;
Expand Down Expand Up @@ -241,7 +229,7 @@ export interface RPCInitOptions {
export interface RPCSYNEvent {
jsonrpc: '2.0';
method: string;
params: any;
params: any[];
id?: string;
}

Expand All @@ -253,7 +241,7 @@ export interface RPCSACKEvent {
}

export interface RPCInvokeOptions {
isNotify: boolean;
isNotify?: boolean;
timeout?: number;
}

Expand Down Expand Up @@ -370,10 +358,10 @@ export class RPC {
const ackEventName = this._getAckEventName(method);
// notify not need ack
if (!synEventData.id) {
handler(synEventData.params);
handler(...synEventData.params);
return;
}
Promise.resolve(handler(synEventData.params))
Promise.resolve(handler(...synEventData.params))
.then((result) => {
const ackEventData: RPCSACKEvent = {
jsonrpc: '2.0',
Expand Down Expand Up @@ -406,12 +394,17 @@ export class RPC {
this._event.off(synEventName);
}

invoke(
method: string,
params: any,
options: RPCInvokeOptions = { isNotify: false, timeout: 0 }
): Promise<any> {
invoke(method: string, ...args: any[] | [...any[], RPCInvokeOptions]): Promise<any> {
return new Promise((resolve, reject) => {
const lastArg = args[args.length - 1];
const hasInvokeOptions =
lastArg &&
typeof lastArg === 'object' &&
(Reflect.has(lastArg, 'isNotify') || Reflect.has(lastArg, 'timeout'));
const options: RPCInvokeOptions = hasInvokeOptions
? lastArg
: { isNotify: false, timeout: 0 };
const params = hasInvokeOptions ? args.slice(0, -1) : args;
const synEventName = this._getSynEventName(method);
const synEventId = RPC.uuid();
const synEventData: RPCSYNEvent = {
Expand Down
27 changes: 27 additions & 0 deletions test/examples/dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { RPCMessageEvent, RPC } from './lib';

(async function () {
const mc = new MessageChannel();
const rpc1 = new RPC({
event: new RPCMessageEvent({
currentEndpoint: mc.port1,
targetEndpoint: mc.port1,
}),
});
const rpc2 = new RPC({
event: new RPCMessageEvent({
currentEndpoint: mc.port2,
targetEndpoint: mc.port2,
}),
});
rpc2.registerMethod('add', (a: number, b: number, c: number) => {
return new Promise((resolve) => {
setTimeout(() => resolve(a + b + c), 400);
});
});
const res1 = await rpc1.invoke('add', 1, 2, 3);
const res2 = await rpc1.invoke('add', 4, 5, 6, { isNotify: true });
const res3 = await rpc1.invoke('add', 7, 8, 9, { timeout: 1000 });
const res4 = await rpc1.invoke('add', 10, 11, 12, { timeout: 4 }).catch((error) => error);
console.log({ res1, res2, res3, res4 });
})();
26 changes: 12 additions & 14 deletions test/examples/index.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Main Page</title>
</head>

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Main Page</title>
</head>

<body>
<div id="app">
<iframe src="/child.html"></iframe>
</div>
<script type="module" src="/index.ts"></script>
</body>

</html>
<body>
<div id="app">
<iframe src="/child.html"></iframe>
</div>
<script type="module" src="/index.ts"></script>
</body>
</html>
6 changes: 4 additions & 2 deletions test/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { RPCMessageEvent, RPC } from './lib';
import { methods, initMainCases, initChildCases } from './cases';
import RPCSelfWorker from './worker?worker';
import RPCSharedWorker from './shared.worker?sharedworker';
import './dev';
import './style.css';

function initIframeCases() {
Expand Down Expand Up @@ -39,8 +40,8 @@ function initSharedWorkerCases() {
const worker = new RPCSharedWorker();
const rpc = new RPC({
event: new RPCMessageEvent({
currentEndpoint: worker.port,
targetEndpoint: worker.port,
currentEndpoint: worker.port as MessagePort,
targetEndpoint: worker.port as MessagePort,
}),
});
return initMainCases({
Expand Down Expand Up @@ -162,6 +163,7 @@ function initAllCases() {
(window as any).runMessageChannelCases = runMessageChannelCases;
(window as any).runBroadcastChannelCases = runBroadcastChannelCases;
// (window as any).runServiceWorkerCases = runServiceWorkerCases;

return () => {
return Promise.all([
runIframeCases(),
Expand Down

0 comments on commit ba76274

Please sign in to comment.