@zimi/remote 0.2.0 → 0.2.1-alpha.11
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 +3 -1
- package/dist/adaptor.d.ts +32 -0
- package/dist/adaptor.js +2 -0
- package/dist/adaptor.js.map +1 -0
- package/dist/adaptors/dao3/client.d.ts +37 -0
- package/dist/adaptors/dao3/client.js +45 -0
- package/dist/adaptors/dao3/client.js.map +1 -0
- package/dist/adaptors/dao3/server.d.ts +80 -0
- package/dist/adaptors/dao3/server.js +105 -0
- package/dist/adaptors/dao3/server.js.map +1 -0
- package/dist/adaptors/http.js +1 -0
- package/dist/adaptors/http.js.map +1 -1
- package/dist/adaptors/iframe.js +1 -0
- package/dist/adaptors/iframe.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/remoteValue/exposeToRemote.d.ts +19 -0
- package/dist/remoteValue/exposeToRemote.js +49 -0
- package/dist/remoteValue/exposeToRemote.js.map +1 -0
- package/dist/remoteValue/remoteValue.d.ts +27 -0
- package/dist/remoteValue/remoteValue.js +68 -0
- package/dist/remoteValue/remoteValue.js.map +1 -0
- package/dist/remoteValue/type.d.ts +66 -0
- package/dist/remoteValue/type.js +2 -0
- package/dist/remoteValue/type.js.map +1 -0
- package/package.json +1 -1
- package/src/{adaptor.d.ts → adaptor.ts} +4 -3
- package/src/adaptors/dao3/client.ts +53 -0
- package/src/adaptors/dao3/server.ts +136 -0
- package/src/adaptors/http.ts +1 -0
- package/src/adaptors/iframe.ts +1 -0
- package/src/index.ts +12 -10
- package/src/remoteValue/exposeToRemote.ts +71 -0
- package/src/remoteValue/remoteValue.ts +94 -0
- package/src/remoteValue/type.ts +124 -0
package/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
- 本地可以是浏览器、服务器,甚至一些受限的 `js` 子集
|
|
4
4
|
- 远端可以是任何终端,如 `iframe` / `Java` 服务器 等
|
|
5
5
|
- 对远端响应的数据格式也不严格限制(可以集中解析)
|
|
6
|
+
- 已在公司游戏前后端通信中应用,极大地降低了通信成本(简化调用)
|
|
7
|
+
- ts 类型严格
|
|
6
8
|
|
|
7
9
|
## install
|
|
8
10
|
```
|
|
@@ -11,7 +13,7 @@ pnpm i @zimi/remote
|
|
|
11
13
|
|
|
12
14
|
## examples
|
|
13
15
|
|
|
14
|
-
###
|
|
16
|
+
### 调用示意
|
|
15
17
|
|
|
16
18
|
```ts
|
|
17
19
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface AdaptorPackageData<D = unknown> {
|
|
2
|
+
/**
|
|
3
|
+
* 自身设备 id,应确保唯一性(对方能凭借该 deviceId 找到该设备)
|
|
4
|
+
*/
|
|
5
|
+
deviceId: string;
|
|
6
|
+
/**
|
|
7
|
+
* 对方的设备 id
|
|
8
|
+
*/
|
|
9
|
+
targetDeviceId: string;
|
|
10
|
+
/**
|
|
11
|
+
* 远程调用的对方的方法名
|
|
12
|
+
*/
|
|
13
|
+
name: string;
|
|
14
|
+
data: D;
|
|
15
|
+
/**
|
|
16
|
+
* 所需回调的方法名(如果需要回调的话)
|
|
17
|
+
*/
|
|
18
|
+
callbackName?: string;
|
|
19
|
+
}
|
|
20
|
+
type Func<D = unknown, R = unknown> = (data: D) => R;
|
|
21
|
+
export type AdaptorCallback = Func<AdaptorPackageData, void>;
|
|
22
|
+
export interface Adaptor {
|
|
23
|
+
every: (callback: AdaptorCallback) => void;
|
|
24
|
+
/**
|
|
25
|
+
* off 用于移除 once 注册的事件,当事件超时后,需要主动 off
|
|
26
|
+
*/
|
|
27
|
+
off: (name: string, callback: AdaptorCallback) => void;
|
|
28
|
+
on: (name: string, callback: AdaptorCallback) => void;
|
|
29
|
+
once: (name: string, callback: AdaptorCallback) => void;
|
|
30
|
+
emit: (data: AdaptorPackageData) => void;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
package/dist/adaptor.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptor.js","sourceRoot":"","sources":["../src/adaptor.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3';
|
|
2
|
+
import { type AdaptorPackageData } from '../../adaptor';
|
|
3
|
+
interface RemoteChannel {
|
|
4
|
+
onClientEvent(fn: (e: unknown) => void): void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 这是 [dao3 游戏](https://dao3.fun/) 代码客户端适配器。
|
|
8
|
+
* ```ts
|
|
9
|
+
* const remoteManager = new ClientSideRemoteChannelEventManager(remoteChannel)
|
|
10
|
+
*
|
|
11
|
+
* export const remoteAdaptor = {
|
|
12
|
+
* every: remoteManager.onEvery.bind(remoteManager),
|
|
13
|
+
* off: remoteManager.off.bind(remoteManager),
|
|
14
|
+
* on: remoteManager.on.bind(remoteManager),
|
|
15
|
+
* once: remoteManager.once.bind(remoteManager),
|
|
16
|
+
* emit: (e) => {
|
|
17
|
+
* remoteChannel.sendServerEvent(e)
|
|
18
|
+
* },
|
|
19
|
+
* } satisfies Adaptor
|
|
20
|
+
*
|
|
21
|
+
* export const remote = new Remote<RemoteFuncsFromClient, RemoteFuncsFromServer>(
|
|
22
|
+
* remoteAdaptor,
|
|
23
|
+
* {
|
|
24
|
+
* deviceId: `client-${Math.random().toString(36).slice(2)}`,
|
|
25
|
+
* }
|
|
26
|
+
* )
|
|
27
|
+
*
|
|
28
|
+
* void remote._.initConnection()
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class ClientSideRemoteChannelEventManager extends EventEmitter<{
|
|
32
|
+
[key: string]: [AdaptorPackageData];
|
|
33
|
+
}> {
|
|
34
|
+
constructor(remoteChannel: RemoteChannel);
|
|
35
|
+
onEvery(fn: (args: AdaptorPackageData) => void): void;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3';
|
|
2
|
+
import { isRemoteAdaptorData } from '../../remote';
|
|
3
|
+
/**
|
|
4
|
+
* 这是 [dao3 游戏](https://dao3.fun/) 代码客户端适配器。
|
|
5
|
+
* ```ts
|
|
6
|
+
* const remoteManager = new ClientSideRemoteChannelEventManager(remoteChannel)
|
|
7
|
+
*
|
|
8
|
+
* export const remoteAdaptor = {
|
|
9
|
+
* every: remoteManager.onEvery.bind(remoteManager),
|
|
10
|
+
* off: remoteManager.off.bind(remoteManager),
|
|
11
|
+
* on: remoteManager.on.bind(remoteManager),
|
|
12
|
+
* once: remoteManager.once.bind(remoteManager),
|
|
13
|
+
* emit: (e) => {
|
|
14
|
+
* remoteChannel.sendServerEvent(e)
|
|
15
|
+
* },
|
|
16
|
+
* } satisfies Adaptor
|
|
17
|
+
*
|
|
18
|
+
* export const remote = new Remote<RemoteFuncsFromClient, RemoteFuncsFromServer>(
|
|
19
|
+
* remoteAdaptor,
|
|
20
|
+
* {
|
|
21
|
+
* deviceId: `client-${Math.random().toString(36).slice(2)}`,
|
|
22
|
+
* }
|
|
23
|
+
* )
|
|
24
|
+
*
|
|
25
|
+
* void remote._.initConnection()
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class ClientSideRemoteChannelEventManager extends EventEmitter {
|
|
29
|
+
constructor(remoteChannel) {
|
|
30
|
+
super();
|
|
31
|
+
remoteChannel.onClientEvent((e) => {
|
|
32
|
+
if (!isRemoteAdaptorData(e)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!e.name.startsWith('__REMOTE_VALUE_REQ__')) {
|
|
36
|
+
this.emit('__remote_every__', e);
|
|
37
|
+
}
|
|
38
|
+
this.emit(e.name, e);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
onEvery(fn) {
|
|
42
|
+
this.on('__remote_every__', fn);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adaptors/dao3/client.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAOlD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,mCAAoC,SAAQ,YAEvD;IACA,YAAY,aAA4B;QACtC,KAAK,EAAE,CAAA;QACP,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5B,OAAM;YACR,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,EAAE,CAAC;gBAC/C,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAA;YAClC,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACtB,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,CAAC,EAAsC;QAC5C,IAAI,CAAC,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3';
|
|
2
|
+
import { type AdaptorPackageData } from '../../adaptor';
|
|
3
|
+
interface GamePlayerEntityLike {
|
|
4
|
+
player: {
|
|
5
|
+
userKey: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
interface RemoteChannel<T extends GamePlayerEntityLike> {
|
|
9
|
+
onServerEvent(fn: (e: {
|
|
10
|
+
args: unknown;
|
|
11
|
+
entity: T;
|
|
12
|
+
}) => void): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 这是 [dao3 游戏](https://dao3.fun/) 代码服务端适配器。
|
|
16
|
+
* ```ts
|
|
17
|
+
* const remoteManager = new ServerSideRemoteChannelEventManager(remoteChannel)
|
|
18
|
+
*
|
|
19
|
+
* world.onPlayerLeave(
|
|
20
|
+
* remoteManager.onPlayerLeave.bind(remoteManager)
|
|
21
|
+
* )
|
|
22
|
+
*
|
|
23
|
+
* export const remoteAdaptor = {
|
|
24
|
+
* every: remoteManager.onEvery.bind(remoteManager),
|
|
25
|
+
* off: remoteManager.off.bind(remoteManager),
|
|
26
|
+
* on: remoteManager.on.bind(remoteManager),
|
|
27
|
+
* once: remoteManager.once.bind(remoteManager),
|
|
28
|
+
* emit: (e) => {
|
|
29
|
+
* const entity = remoteManager.getEntity(e.targetDeviceId)
|
|
30
|
+
* if (!entity) {
|
|
31
|
+
* console.error('entity not found')
|
|
32
|
+
* return
|
|
33
|
+
* }
|
|
34
|
+
* remoteChannel.sendClientEvent(entity, e as unknown as JSONValue)
|
|
35
|
+
* },
|
|
36
|
+
* } satisfies Adaptor
|
|
37
|
+
*
|
|
38
|
+
* export const remote = new Remote<RemoteFuncsFromServer, RemoteFuncsFromClient>(
|
|
39
|
+
* remoteAdaptor,
|
|
40
|
+
* {
|
|
41
|
+
* deviceId: 'server',
|
|
42
|
+
* }
|
|
43
|
+
* )
|
|
44
|
+
*
|
|
45
|
+
* remote.register('initConnection', async () => {
|
|
46
|
+
* // 什么也不干,单纯的让客户端在服务端“备案”
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* export const getEntity = remoteManager.getEntity.bind(remoteManager)
|
|
50
|
+
* export const getIdByEntity = remoteManager.getIdByEntity.bind(remoteManager)
|
|
51
|
+
* export const waitForRegister = remoteManager.waitForRegister.bind(remoteManager)
|
|
52
|
+
* export const remoteTo = remoteManager.remoteTo.bind(remoteManager)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare class ServerSideRemoteChannelEventManager<T extends GamePlayerEntityLike> extends EventEmitter<{
|
|
56
|
+
[key: string]: [AdaptorPackageData];
|
|
57
|
+
}> {
|
|
58
|
+
entityMap: [string, T][];
|
|
59
|
+
constructor(remoteChannel: RemoteChannel<T>);
|
|
60
|
+
onPlayerLeave(e: {
|
|
61
|
+
entity: T;
|
|
62
|
+
}): void;
|
|
63
|
+
onEvery(fn: (args: AdaptorPackageData) => void): void;
|
|
64
|
+
waitForRegister(entity: T): Promise<void>;
|
|
65
|
+
getEntity(deviceId: string): T | undefined;
|
|
66
|
+
getIdByEntity(entity: T): string | undefined;
|
|
67
|
+
remoteTo(config: {
|
|
68
|
+
target: T;
|
|
69
|
+
}): {
|
|
70
|
+
targetDeviceId: string;
|
|
71
|
+
};
|
|
72
|
+
remoteTo(config: {
|
|
73
|
+
target: T;
|
|
74
|
+
timeoutMs: number;
|
|
75
|
+
}): {
|
|
76
|
+
targetDeviceId: string;
|
|
77
|
+
timeoutMs: number;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3';
|
|
2
|
+
import { isRemoteAdaptorData } from '../../remote';
|
|
3
|
+
import { isRemoteValueEvent } from '../../remoteValue/exposeToRemote';
|
|
4
|
+
/**
|
|
5
|
+
* 这是 [dao3 游戏](https://dao3.fun/) 代码服务端适配器。
|
|
6
|
+
* ```ts
|
|
7
|
+
* const remoteManager = new ServerSideRemoteChannelEventManager(remoteChannel)
|
|
8
|
+
*
|
|
9
|
+
* world.onPlayerLeave(
|
|
10
|
+
* remoteManager.onPlayerLeave.bind(remoteManager)
|
|
11
|
+
* )
|
|
12
|
+
*
|
|
13
|
+
* export const remoteAdaptor = {
|
|
14
|
+
* every: remoteManager.onEvery.bind(remoteManager),
|
|
15
|
+
* off: remoteManager.off.bind(remoteManager),
|
|
16
|
+
* on: remoteManager.on.bind(remoteManager),
|
|
17
|
+
* once: remoteManager.once.bind(remoteManager),
|
|
18
|
+
* emit: (e) => {
|
|
19
|
+
* const entity = remoteManager.getEntity(e.targetDeviceId)
|
|
20
|
+
* if (!entity) {
|
|
21
|
+
* console.error('entity not found')
|
|
22
|
+
* return
|
|
23
|
+
* }
|
|
24
|
+
* remoteChannel.sendClientEvent(entity, e as unknown as JSONValue)
|
|
25
|
+
* },
|
|
26
|
+
* } satisfies Adaptor
|
|
27
|
+
*
|
|
28
|
+
* export const remote = new Remote<RemoteFuncsFromServer, RemoteFuncsFromClient>(
|
|
29
|
+
* remoteAdaptor,
|
|
30
|
+
* {
|
|
31
|
+
* deviceId: 'server',
|
|
32
|
+
* }
|
|
33
|
+
* )
|
|
34
|
+
*
|
|
35
|
+
* remote.register('initConnection', async () => {
|
|
36
|
+
* // 什么也不干,单纯的让客户端在服务端“备案”
|
|
37
|
+
* })
|
|
38
|
+
*
|
|
39
|
+
* export const getEntity = remoteManager.getEntity.bind(remoteManager)
|
|
40
|
+
* export const getIdByEntity = remoteManager.getIdByEntity.bind(remoteManager)
|
|
41
|
+
* export const waitForRegister = remoteManager.waitForRegister.bind(remoteManager)
|
|
42
|
+
* export const remoteTo = remoteManager.remoteTo.bind(remoteManager)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export class ServerSideRemoteChannelEventManager extends EventEmitter {
|
|
46
|
+
constructor(remoteChannel) {
|
|
47
|
+
super();
|
|
48
|
+
Object.defineProperty(this, "entityMap", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true,
|
|
52
|
+
value: []
|
|
53
|
+
});
|
|
54
|
+
remoteChannel.onServerEvent((e) => {
|
|
55
|
+
const { args } = e;
|
|
56
|
+
if (!isRemoteAdaptorData(args)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const prevItem = this.entityMap.find((item) => item[1].player.userKey === e.entity.player.userKey);
|
|
60
|
+
if (prevItem) {
|
|
61
|
+
prevItem[0] = args.deviceId;
|
|
62
|
+
prevItem[1] = e.entity;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.entityMap.push([args.deviceId, e.entity]);
|
|
66
|
+
}
|
|
67
|
+
if (!isRemoteValueEvent(args.name)) {
|
|
68
|
+
this.emit('__remote_every__', args);
|
|
69
|
+
}
|
|
70
|
+
this.emit(args.name, args);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
onPlayerLeave(e) {
|
|
74
|
+
this.entityMap = this.entityMap.filter((item) => item[1].player.userKey !== e.entity.player.userKey);
|
|
75
|
+
}
|
|
76
|
+
onEvery(fn) {
|
|
77
|
+
this.on('__remote_every__', fn);
|
|
78
|
+
}
|
|
79
|
+
waitForRegister(entity) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
if (this.entityMap.some(([_, en]) => en.player.userKey === entity.player.userKey)) {
|
|
82
|
+
resolve();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.once('__remote_every__', () => {
|
|
86
|
+
resolve();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
getEntity(deviceId) {
|
|
91
|
+
var _a;
|
|
92
|
+
return (_a = this.entityMap.find((item) => item[0] === deviceId)) === null || _a === void 0 ? void 0 : _a[1];
|
|
93
|
+
}
|
|
94
|
+
getIdByEntity(entity) {
|
|
95
|
+
var _a;
|
|
96
|
+
return (_a = this.entityMap.find((item) => item[1].player.userKey === entity.player.userKey)) === null || _a === void 0 ? void 0 : _a[0];
|
|
97
|
+
}
|
|
98
|
+
remoteTo({ target, ...rest }) {
|
|
99
|
+
return {
|
|
100
|
+
targetDeviceId: this.getIdByEntity(target),
|
|
101
|
+
...rest,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/adaptors/dao3/server.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAarE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,OAAO,mCAEX,SAAQ,YAER;IAGA,YAAY,aAA+B;QACzC,KAAK,EAAE,CAAA;QAHT;;;;mBAA2B,EAAE;WAAA;QAI3B,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;YAClB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,OAAM;YACR,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAClC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAC7D,CAAA;YACD,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAA;gBAC3B,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA;YACxB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;YAChD,CAAC;YACD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAA;YACrC,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,aAAa,CAAC,CAAgB;QAC5B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CACpC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAC7D,CAAA;IACH,CAAC;IAED,OAAO,CAAC,EAAsC;QAC5C,IAAI,CAAC,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;IAED,eAAe,CAAC,MAAS;QACvB,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IACE,IAAI,CAAC,SAAS,CAAC,IAAI,CACjB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CACzD,EACD,CAAC;gBACD,OAAO,EAAE,CAAA;gBACT,OAAM;YACR,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE;gBACjC,OAAO,EAAE,CAAA;YACX,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAC,QAAgB;;QACxB,OAAO,MAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,0CAAG,CAAC,CAAC,CAAA;IACjE,CAAC;IAED,aAAa,CAAC,MAAS;;QACrB,OAAO,MAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAC3D,0CAAG,CAAC,CAAC,CAAA;IACR,CAAC;IASD,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAqC;QAC7D,OAAO;YACL,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;YAC1C,GAAG,IAAI;SACR,CAAA;IACH,CAAC;CACF"}
|
package/dist/adaptors/http.js
CHANGED
|
@@ -17,6 +17,7 @@ export const remoteEventManager = new RemoteEventManager();
|
|
|
17
17
|
export function createHttpAdaptor({ onEmit, }) {
|
|
18
18
|
const adaptor = {
|
|
19
19
|
every: remoteEventManager.onEvery.bind(remoteEventManager),
|
|
20
|
+
on: remoteEventManager.on.bind(remoteEventManager),
|
|
20
21
|
once: remoteEventManager.once.bind(remoteEventManager),
|
|
21
22
|
off: remoteEventManager.off.bind(remoteEventManager),
|
|
22
23
|
emit: onEmit,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/adaptors/http.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AAIxC,MAAM,kBAAmB,SAAQ,YAE/B;IAFF;;QAGE;;;;mBAAmB,kBAAkB;WAAA;IAKvC,CAAC;IAHC,OAAO,CAAC,EAAsC;QAC5C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;IACpC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAA;AAE1D,MAAM,UAAU,iBAAiB,CAAC,EAChC,MAAM,GAGP;IACC,MAAM,OAAO,GAAY;QACvB,KAAK,EAAE,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAC1D,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACtD,GAAG,EAAE,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACpD,IAAI,EAAE,MAAM;KACb,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/adaptors/http.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AAIxC,MAAM,kBAAmB,SAAQ,YAE/B;IAFF;;QAGE;;;;mBAAmB,kBAAkB;WAAA;IAKvC,CAAC;IAHC,OAAO,CAAC,EAAsC;QAC5C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;IACpC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAA;AAE1D,MAAM,UAAU,iBAAiB,CAAC,EAChC,MAAM,GAGP;IACC,MAAM,OAAO,GAAY;QACvB,KAAK,EAAE,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAC1D,EAAE,EAAE,kBAAkB,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAClD,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACtD,GAAG,EAAE,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACpD,IAAI,EAAE,MAAM;KACb,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
package/dist/adaptors/iframe.js
CHANGED
|
@@ -29,6 +29,7 @@ export function createIframeAdaptor({ onEmit, }) {
|
|
|
29
29
|
const remoteEventManager = new RemoteEventManager();
|
|
30
30
|
const adaptor = {
|
|
31
31
|
every: remoteEventManager.onEvery.bind(remoteEventManager),
|
|
32
|
+
on: remoteEventManager.on.bind(remoteEventManager),
|
|
32
33
|
once: remoteEventManager.once.bind(remoteEventManager),
|
|
33
34
|
off: remoteEventManager.off.bind(remoteEventManager),
|
|
34
35
|
emit: onEmit,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iframe.js","sourceRoot":"","sources":["../../src/adaptors/iframe.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAI/C,MAAM,kBAAmB,SAAQ,YAE/B;IAGA;QACE,KAAK,EAAE,CAAA;QAHT;;;;mBAAmB,kBAAkB;WAAA;QAInC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAM;QACR,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3C,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;YACtB,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;gBAC1B,oCAAoC;gBACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;YACxC,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,CAAC,EAAsC;QAC5C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;IACpC,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,EAClC,MAAM,GAGP;IACC,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAA;IAEnD,MAAM,OAAO,GAAY;QACvB,KAAK,EAAE,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAC1D,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACtD,GAAG,EAAE,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACpD,IAAI,EAAE,MAAM;KACb,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"iframe.js","sourceRoot":"","sources":["../../src/adaptors/iframe.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAI/C,MAAM,kBAAmB,SAAQ,YAE/B;IAGA;QACE,KAAK,EAAE,CAAA;QAHT;;;;mBAAmB,kBAAkB;WAAA;QAInC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAM;QACR,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3C,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;YACtB,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;gBAC1B,oCAAoC;gBACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;YACxC,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,CAAC,EAAsC;QAC5C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;IACpC,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,EAClC,MAAM,GAGP;IACC,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAA;IAEnD,MAAM,OAAO,GAAY;QACvB,KAAK,EAAE,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAC1D,EAAE,EAAE,kBAAkB,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAClD,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACtD,GAAG,EAAE,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC;QACpD,IAAI,EAAE,MAAM;KACb,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
1
|
+
export * from './remote';
|
|
2
|
+
export * from './adaptor';
|
|
3
|
+
export * from './response';
|
|
4
|
+
export * from './remoteValue/remoteValue';
|
|
5
|
+
export * from './remoteValue/exposeToRemote';
|
|
6
|
+
export type { ToFunc, RemoteCallData } from './remoteValue/type';
|
|
7
|
+
export * from './adaptors/iframe';
|
|
8
|
+
export * from './adaptors/http';
|
|
9
|
+
export * from './adaptors/dao3/client';
|
|
10
|
+
export * from './adaptors/dao3/server';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export * from './remote';
|
|
2
|
+
export * from './adaptor';
|
|
3
|
+
export * from './response';
|
|
4
|
+
export * from './remoteValue/remoteValue';
|
|
5
|
+
export * from './remoteValue/exposeToRemote';
|
|
6
|
+
export * from './adaptors/iframe';
|
|
7
|
+
export * from './adaptors/http';
|
|
8
|
+
export * from './adaptors/dao3/client';
|
|
9
|
+
export * from './adaptors/dao3/server';
|
|
5
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAE1B,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAG5C,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Adaptor, AdaptorPackageData } from '../adaptor';
|
|
2
|
+
import { RemoteCallData } from './type';
|
|
3
|
+
type AdaptorData = AdaptorPackageData<RemoteCallData>;
|
|
4
|
+
interface ExposeProps {
|
|
5
|
+
globalName: string;
|
|
6
|
+
adaptor: Adaptor;
|
|
7
|
+
/**
|
|
8
|
+
* ['*'] or ['device_id_1', 'device_id_2']
|
|
9
|
+
*/
|
|
10
|
+
exposeTo: string[];
|
|
11
|
+
/**
|
|
12
|
+
* 你可以在该回调中抛错,以阻止远程调用,
|
|
13
|
+
* 也可以修改传入的 data
|
|
14
|
+
*/
|
|
15
|
+
onRequest?: (e: AdaptorData) => RemoteCallData | Promise<RemoteCallData>;
|
|
16
|
+
}
|
|
17
|
+
export declare function isRemoteValueEvent(eventName: string): boolean;
|
|
18
|
+
export declare function exposeToRemote<T extends object>(obj: T, options: ExposeProps): () => void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RemoteError, response } from '../response';
|
|
2
|
+
function defaultOnRequest(e) {
|
|
3
|
+
return e.data;
|
|
4
|
+
}
|
|
5
|
+
export function isRemoteValueEvent(eventName) {
|
|
6
|
+
return eventName.startsWith('__REMOTE_VALUE_REQ__');
|
|
7
|
+
}
|
|
8
|
+
export function exposeToRemote(obj, options) {
|
|
9
|
+
const { globalName, adaptor, exposeTo, onRequest = defaultOnRequest, } = options;
|
|
10
|
+
const callback = async (e) => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
try {
|
|
13
|
+
const { paths, args } = await onRequest(e);
|
|
14
|
+
if (!exposeTo.includes(e.deviceId) && !exposeTo.includes('*')) {
|
|
15
|
+
throw new RemoteError('permission denied');
|
|
16
|
+
}
|
|
17
|
+
let target = obj;
|
|
18
|
+
for (let i = 0; i < paths.length; i += 1) {
|
|
19
|
+
target = target[paths[i]];
|
|
20
|
+
}
|
|
21
|
+
let res;
|
|
22
|
+
if (target instanceof Function) {
|
|
23
|
+
res = await target(...args);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
res = target;
|
|
27
|
+
}
|
|
28
|
+
adaptor.emit({
|
|
29
|
+
name: (_a = e.callbackName) !== null && _a !== void 0 ? _a : 'IMPOSSIBLE_NO_CALLBACK_NAME',
|
|
30
|
+
deviceId: e.targetDeviceId,
|
|
31
|
+
targetDeviceId: e.deviceId,
|
|
32
|
+
data: response.success(res),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
adaptor.emit({
|
|
37
|
+
name: (_b = e.callbackName) !== null && _b !== void 0 ? _b : 'IMPOSSIBLE_NO_CALLBACK_NAME',
|
|
38
|
+
deviceId: e.targetDeviceId,
|
|
39
|
+
targetDeviceId: e.deviceId,
|
|
40
|
+
data: response.error(RemoteError.fromError(error)),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
adaptor.on(`__REMOTE_VALUE_REQ__${globalName}`, callback);
|
|
45
|
+
return () => {
|
|
46
|
+
adaptor.off(`__REMOTE_VALUE_REQ__${globalName}`, callback);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=exposeToRemote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exposeToRemote.js","sourceRoot":"","sources":["../../src/remoteValue/exposeToRemote.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAmBnD,SAAS,gBAAgB,CAAC,CAAc;IACtC,OAAO,CAAC,CAAC,IAAI,CAAA;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,SAAS,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,cAAc,CAAmB,GAAM,EAAE,OAAoB;IAC3E,MAAM,EACJ,UAAU,EACV,OAAO,EACP,QAAQ,EACR,SAAS,GAAG,gBAAgB,GAC7B,GAAG,OAAO,CAAA;IACX,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAqB,EAAE,EAAE;;QAC/C,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,CAAC,CAAgB,CAAC,CAAA;YACzD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9D,MAAM,IAAI,WAAW,CAAC,mBAAmB,CAAC,CAAA;YAC5C,CAAC;YACD,IAAI,MAAM,GAAG,GAAG,CAAA;YAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAwB,CAAkB,CAAA;YACnE,CAAC;YACD,IAAI,GAAY,CAAA;YAChB,IAAI,MAAM,YAAY,QAAQ,EAAE,CAAC;gBAC/B,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;YAC7B,CAAC;iBAAM,CAAC;gBACN,GAAG,GAAG,MAAM,CAAA;YACd,CAAC;YACD,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAA,CAAC,CAAC,YAAY,mCAAI,6BAA6B;gBACrD,QAAQ,EAAE,CAAC,CAAC,cAAc;gBAC1B,cAAc,EAAE,CAAC,CAAC,QAAQ;gBAC1B,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;aAC5B,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAA,CAAC,CAAC,YAAY,mCAAI,6BAA6B;gBACrD,QAAQ,EAAE,CAAC,CAAC,cAAc;gBAC1B,cAAc,EAAE,CAAC,CAAC,QAAQ;gBAC1B,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;aACnD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAA;IACD,OAAO,CAAC,EAAE,CAAC,uBAAuB,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAA;IACzD,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,GAAG,CAAC,uBAAuB,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAA;IAC5D,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Adaptor, AdaptorPackageData } from '../adaptor';
|
|
2
|
+
import type { ToFunc } from './type';
|
|
3
|
+
type RemoteValueProps = Pick<AdaptorPackageData, 'deviceId' | 'targetDeviceId'> & {
|
|
4
|
+
globalName: string;
|
|
5
|
+
adaptor: Adaptor;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
log?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* @example
|
|
11
|
+
* ```
|
|
12
|
+
* const obj = remoteValue<{
|
|
13
|
+
* a: number;
|
|
14
|
+
* b: {
|
|
15
|
+
* c: string;
|
|
16
|
+
* };
|
|
17
|
+
* funcD: () => number
|
|
18
|
+
* }>()
|
|
19
|
+
*
|
|
20
|
+
* const val_1 = await obj.a() // number
|
|
21
|
+
* const val_2 = await obj.b() // { c: string }
|
|
22
|
+
* const val_3 = await obj.b.c() // string
|
|
23
|
+
* const val_4 = await obj.funcD() // number
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function remoteValue<T extends object>(props: RemoteValueProps): ToFunc<T>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { RemoteError, RemoteTimeoutError } from '../response';
|
|
2
|
+
function noop() {
|
|
3
|
+
// pass
|
|
4
|
+
}
|
|
5
|
+
function geneProxy(paths, props) {
|
|
6
|
+
return new Proxy(noop, {
|
|
7
|
+
apply(target, thisArg, argArray) {
|
|
8
|
+
const { adaptor, globalName, timeoutMs = 30000, log = false, ...restProps } = props;
|
|
9
|
+
const randomStr = Math.random().toString(36).slice(2);
|
|
10
|
+
const name = `__REMOTE_VALUE_REQ__${globalName}`;
|
|
11
|
+
const responseName = `__REMOTE_VALUE_RES__${[globalName, ...paths].join('.')}-${randomStr}`;
|
|
12
|
+
if (log) {
|
|
13
|
+
console.log(`[remoteValue] [${restProps.deviceId}] 正在访问远端 [${restProps.targetDeviceId}] 的变量: "${[globalName, ...paths].join('.')}"`);
|
|
14
|
+
}
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
let timer;
|
|
17
|
+
const callback = (e) => {
|
|
18
|
+
var _a;
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
if (RemoteError.isRemoteError(e.data)) {
|
|
21
|
+
reject(RemoteError.fromError(e.data));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
resolve((_a = e.data) === null || _a === void 0 ? void 0 : _a.data);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
adaptor.once(responseName, callback);
|
|
28
|
+
timer = setTimeout(() => {
|
|
29
|
+
adaptor.off(responseName, callback);
|
|
30
|
+
reject(new RemoteTimeoutError(`timeout: ${[globalName, ...paths].join('.')}`));
|
|
31
|
+
}, timeoutMs);
|
|
32
|
+
adaptor.emit({
|
|
33
|
+
name,
|
|
34
|
+
callbackName: responseName,
|
|
35
|
+
...restProps,
|
|
36
|
+
data: {
|
|
37
|
+
paths,
|
|
38
|
+
args: argArray,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
get(target, prop) {
|
|
44
|
+
return geneProxy([...paths, prop], props);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @example
|
|
50
|
+
* ```
|
|
51
|
+
* const obj = remoteValue<{
|
|
52
|
+
* a: number;
|
|
53
|
+
* b: {
|
|
54
|
+
* c: string;
|
|
55
|
+
* };
|
|
56
|
+
* funcD: () => number
|
|
57
|
+
* }>()
|
|
58
|
+
*
|
|
59
|
+
* const val_1 = await obj.a() // number
|
|
60
|
+
* const val_2 = await obj.b() // { c: string }
|
|
61
|
+
* const val_3 = await obj.b.c() // string
|
|
62
|
+
* const val_4 = await obj.funcD() // number
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function remoteValue(props) {
|
|
66
|
+
return geneProxy([], props);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=remoteValue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteValue.js","sourceRoot":"","sources":["../../src/remoteValue/remoteValue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAY,MAAM,aAAa,CAAA;AAIvE,SAAS,IAAI;IACX,OAAO;AACT,CAAC;AAYD,SAAS,SAAS,CAAmB,KAAe,EAAE,KAAuB;IAC3E,OAAO,IAAI,KAAK,CAAY,IAA4B,EAAE;QACxD,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ;YAC7B,MAAM,EACJ,OAAO,EACP,UAAU,EACV,SAAS,GAAG,KAAK,EACjB,GAAG,GAAG,KAAK,EACX,GAAG,SAAS,EACb,GAAG,KAAK,CAAA;YACT,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACrD,MAAM,IAAI,GAAG,uBAAuB,UAAU,EAAE,CAAA;YAChD,MAAM,YAAY,GAAG,uBAAuB,CAAC,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,EAAE,CAAA;YAE3F,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,GAAG,CACT,kBAAkB,SAAS,CAAC,QAAQ,aAAa,SAAS,CAAC,cAAc,WAAW,CAAC,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CACxH,CAAA;YACH,CAAC;YAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,IAAI,KAAiC,CAAA;gBACrC,MAAM,QAAQ,GAAoB,CAAC,CAAC,EAAE,EAAE;;oBACtC,YAAY,CAAC,KAAK,CAAC,CAAA;oBACnB,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;wBACtC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;oBACvC,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,MAAC,CAAC,CAAC,IAA4C,0CAAE,IAAI,CAAC,CAAA;oBAChE,CAAC;gBACH,CAAC,CAAA;gBACD,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;gBACpC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;oBACtB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;oBACnC,MAAM,CACJ,IAAI,kBAAkB,CACpB,YAAY,CAAC,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAC/C,CACF,CAAA;gBACH,CAAC,EAAE,SAAS,CAAC,CAAA;gBACb,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI;oBACJ,YAAY,EAAE,YAAY;oBAC1B,GAAG,SAAS;oBACZ,IAAI,EAAE;wBACJ,KAAK;wBACL,IAAI,EAAE,QAAQ;qBACU;iBAC3B,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,IAAI;YACd,OAAO,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,IAAc,CAAC,EAAE,KAAK,CAAC,CAAA;QACrD,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,WAAW,CAAmB,KAAuB;IACnE,OAAO,SAAS,CAAI,EAAE,EAAE,KAAK,CAAC,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
type HasFunc<T> = T extends (...args: unknown[]) => unknown ? true : T extends unknown[] ? {
|
|
2
|
+
[K in keyof T]: HasFunc<T[K]>;
|
|
3
|
+
}[number] extends false ? false : true : T extends object ? {
|
|
4
|
+
[K in keyof T]: HasFunc<T[K]>;
|
|
5
|
+
}[keyof T] extends false ? false : true : false;
|
|
6
|
+
export type ToFunc<T extends object> = {
|
|
7
|
+
[K in keyof T]: T[K] extends (...args: infer Args) => infer Ret ? (...args: Args) => Promise<Awaited<Ret>> : HasFunc<T[K]> extends false ? () => Promise<T[K]> : T[K] extends object ? ToFunc<T[K]> : never;
|
|
8
|
+
};
|
|
9
|
+
export interface RemoteCallData {
|
|
10
|
+
paths: string[];
|
|
11
|
+
args: unknown[];
|
|
12
|
+
}
|
|
13
|
+
type Assert<T extends true> = T;
|
|
14
|
+
interface TestObj {
|
|
15
|
+
a1: number;
|
|
16
|
+
a2: number[];
|
|
17
|
+
a3: [number, string];
|
|
18
|
+
a4: {
|
|
19
|
+
b: number;
|
|
20
|
+
c: [string];
|
|
21
|
+
};
|
|
22
|
+
a5: () => Promise<number>;
|
|
23
|
+
a6: () => Promise<number[]>;
|
|
24
|
+
a7: () => Promise<[number, string]>;
|
|
25
|
+
b: {
|
|
26
|
+
b1: number;
|
|
27
|
+
b2: number[];
|
|
28
|
+
b3: [number, string];
|
|
29
|
+
b4: () => {
|
|
30
|
+
b: number;
|
|
31
|
+
c: [string];
|
|
32
|
+
};
|
|
33
|
+
b5: () => Promise<number>;
|
|
34
|
+
b6: () => Promise<number[]>;
|
|
35
|
+
b7: () => Promise<[number, string]>;
|
|
36
|
+
};
|
|
37
|
+
f: {
|
|
38
|
+
b: number;
|
|
39
|
+
c: {
|
|
40
|
+
d: () => number;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export type TestA1 = Assert<ToFunc<TestObj>['a1'] extends () => Promise<number> ? true : false>;
|
|
45
|
+
export type TestA2 = Assert<ToFunc<TestObj>['a2'] extends () => Promise<number[]> ? true : false>;
|
|
46
|
+
export type TestA3 = Assert<ToFunc<TestObj>['a3'] extends () => Promise<[number, string]> ? true : false>;
|
|
47
|
+
export type TestA4 = Assert<ToFunc<TestObj>['a4'] extends () => Promise<{
|
|
48
|
+
b: number;
|
|
49
|
+
c: [string];
|
|
50
|
+
}> ? true : false>;
|
|
51
|
+
export type TestA5 = Assert<ToFunc<TestObj>['a5'] extends () => Promise<number> ? true : false>;
|
|
52
|
+
export type TestA6 = Assert<ToFunc<TestObj>['a6'] extends () => Promise<number[]> ? true : false>;
|
|
53
|
+
export type TestA7 = Assert<ToFunc<TestObj>['a7'] extends () => Promise<[number, string]> ? true : false>;
|
|
54
|
+
export type TestB1 = Assert<ToFunc<TestObj>['b']['b1'] extends () => Promise<number> ? true : false>;
|
|
55
|
+
export type TestB2 = Assert<ToFunc<TestObj>['b']['b2'] extends () => Promise<number[]> ? true : false>;
|
|
56
|
+
export type TestB3 = Assert<ToFunc<TestObj>['b']['b3'] extends () => Promise<[number, string]> ? true : false>;
|
|
57
|
+
export type TestB4 = Assert<ToFunc<TestObj>['b']['b4'] extends () => Promise<{
|
|
58
|
+
b: number;
|
|
59
|
+
c: [string];
|
|
60
|
+
}> ? true : false>;
|
|
61
|
+
export type TestB5 = Assert<ToFunc<TestObj>['b']['b5'] extends () => Promise<number> ? true : false>;
|
|
62
|
+
export type TestB6 = Assert<ToFunc<TestObj>['b']['b6'] extends () => Promise<number[]> ? true : false>;
|
|
63
|
+
export type TestB7 = Assert<ToFunc<TestObj>['b']['b7'] extends () => Promise<[number, string]> ? true : false>;
|
|
64
|
+
export type TestF = Assert<ToFunc<TestObj>['f']['b'] extends () => Promise<number> ? true : false>;
|
|
65
|
+
export type TestF2 = Assert<ToFunc<TestObj>['f']['c']['d'] extends () => Promise<number> ? true : false>;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/remoteValue/type.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export interface AdaptorPackageData {
|
|
1
|
+
export interface AdaptorPackageData<D = unknown> {
|
|
2
2
|
/**
|
|
3
3
|
* 自身设备 id,应确保唯一性(对方能凭借该 deviceId 找到该设备)
|
|
4
4
|
*/
|
|
@@ -11,7 +11,7 @@ export interface AdaptorPackageData {
|
|
|
11
11
|
* 远程调用的对方的方法名
|
|
12
12
|
*/
|
|
13
13
|
name: string
|
|
14
|
-
data:
|
|
14
|
+
data: D
|
|
15
15
|
/**
|
|
16
16
|
* 所需回调的方法名(如果需要回调的话)
|
|
17
17
|
*/
|
|
@@ -20,7 +20,7 @@ export interface AdaptorPackageData {
|
|
|
20
20
|
|
|
21
21
|
type Func<D = unknown, R = unknown> = (data: D) => R
|
|
22
22
|
|
|
23
|
-
type AdaptorCallback = Func<AdaptorPackageData, void>
|
|
23
|
+
export type AdaptorCallback = Func<AdaptorPackageData, void>
|
|
24
24
|
|
|
25
25
|
export interface Adaptor {
|
|
26
26
|
every: (callback: AdaptorCallback) => void
|
|
@@ -28,6 +28,7 @@ export interface Adaptor {
|
|
|
28
28
|
* off 用于移除 once 注册的事件,当事件超时后,需要主动 off
|
|
29
29
|
*/
|
|
30
30
|
off: (name: string, callback: AdaptorCallback) => void
|
|
31
|
+
on: (name: string, callback: AdaptorCallback) => void
|
|
31
32
|
once: (name: string, callback: AdaptorCallback) => void
|
|
32
33
|
emit: (data: AdaptorPackageData) => void
|
|
33
34
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3'
|
|
2
|
+
import { isRemoteAdaptorData } from '../../remote'
|
|
3
|
+
import { type AdaptorPackageData } from '../../adaptor'
|
|
4
|
+
|
|
5
|
+
interface RemoteChannel {
|
|
6
|
+
onClientEvent(fn: (e: unknown) => void): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 这是 [dao3 游戏](https://dao3.fun/) 代码客户端适配器。
|
|
11
|
+
* ```ts
|
|
12
|
+
* const remoteManager = new ClientSideRemoteChannelEventManager(remoteChannel)
|
|
13
|
+
*
|
|
14
|
+
* export const remoteAdaptor = {
|
|
15
|
+
* every: remoteManager.onEvery.bind(remoteManager),
|
|
16
|
+
* off: remoteManager.off.bind(remoteManager),
|
|
17
|
+
* on: remoteManager.on.bind(remoteManager),
|
|
18
|
+
* once: remoteManager.once.bind(remoteManager),
|
|
19
|
+
* emit: (e) => {
|
|
20
|
+
* remoteChannel.sendServerEvent(e)
|
|
21
|
+
* },
|
|
22
|
+
* } satisfies Adaptor
|
|
23
|
+
*
|
|
24
|
+
* export const remote = new Remote<RemoteFuncsFromClient, RemoteFuncsFromServer>(
|
|
25
|
+
* remoteAdaptor,
|
|
26
|
+
* {
|
|
27
|
+
* deviceId: `client-${Math.random().toString(36).slice(2)}`,
|
|
28
|
+
* }
|
|
29
|
+
* )
|
|
30
|
+
*
|
|
31
|
+
* void remote._.initConnection()
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class ClientSideRemoteChannelEventManager extends EventEmitter<{
|
|
35
|
+
[key: string]: [AdaptorPackageData]
|
|
36
|
+
}> {
|
|
37
|
+
constructor(remoteChannel: RemoteChannel) {
|
|
38
|
+
super()
|
|
39
|
+
remoteChannel.onClientEvent((e) => {
|
|
40
|
+
if (!isRemoteAdaptorData(e)) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if (!e.name.startsWith('__REMOTE_VALUE_REQ__')) {
|
|
44
|
+
this.emit('__remote_every__', e)
|
|
45
|
+
}
|
|
46
|
+
this.emit(e.name, e)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
onEvery(fn: (args: AdaptorPackageData) => void): void {
|
|
51
|
+
this.on('__remote_every__', fn)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3'
|
|
2
|
+
import { isRemoteAdaptorData } from '../../remote'
|
|
3
|
+
import { isRemoteValueEvent } from '../../remoteValue/exposeToRemote'
|
|
4
|
+
import { type AdaptorPackageData } from '../../adaptor'
|
|
5
|
+
|
|
6
|
+
interface GamePlayerEntityLike {
|
|
7
|
+
player: {
|
|
8
|
+
userKey: string
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface RemoteChannel<T extends GamePlayerEntityLike> {
|
|
13
|
+
onServerEvent(fn: (e: { args: unknown; entity: T }) => void): void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 这是 [dao3 游戏](https://dao3.fun/) 代码服务端适配器。
|
|
18
|
+
* ```ts
|
|
19
|
+
* const remoteManager = new ServerSideRemoteChannelEventManager(remoteChannel)
|
|
20
|
+
*
|
|
21
|
+
* world.onPlayerLeave(
|
|
22
|
+
* remoteManager.onPlayerLeave.bind(remoteManager)
|
|
23
|
+
* )
|
|
24
|
+
*
|
|
25
|
+
* export const remoteAdaptor = {
|
|
26
|
+
* every: remoteManager.onEvery.bind(remoteManager),
|
|
27
|
+
* off: remoteManager.off.bind(remoteManager),
|
|
28
|
+
* on: remoteManager.on.bind(remoteManager),
|
|
29
|
+
* once: remoteManager.once.bind(remoteManager),
|
|
30
|
+
* emit: (e) => {
|
|
31
|
+
* const entity = remoteManager.getEntity(e.targetDeviceId)
|
|
32
|
+
* if (!entity) {
|
|
33
|
+
* console.error('entity not found')
|
|
34
|
+
* return
|
|
35
|
+
* }
|
|
36
|
+
* remoteChannel.sendClientEvent(entity, e as unknown as JSONValue)
|
|
37
|
+
* },
|
|
38
|
+
* } satisfies Adaptor
|
|
39
|
+
*
|
|
40
|
+
* export const remote = new Remote<RemoteFuncsFromServer, RemoteFuncsFromClient>(
|
|
41
|
+
* remoteAdaptor,
|
|
42
|
+
* {
|
|
43
|
+
* deviceId: 'server',
|
|
44
|
+
* }
|
|
45
|
+
* )
|
|
46
|
+
*
|
|
47
|
+
* remote.register('initConnection', async () => {
|
|
48
|
+
* // 什么也不干,单纯的让客户端在服务端“备案”
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* export const getEntity = remoteManager.getEntity.bind(remoteManager)
|
|
52
|
+
* export const getIdByEntity = remoteManager.getIdByEntity.bind(remoteManager)
|
|
53
|
+
* export const waitForRegister = remoteManager.waitForRegister.bind(remoteManager)
|
|
54
|
+
* export const remoteTo = remoteManager.remoteTo.bind(remoteManager)
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export class ServerSideRemoteChannelEventManager<
|
|
58
|
+
T extends GamePlayerEntityLike,
|
|
59
|
+
> extends EventEmitter<{
|
|
60
|
+
[key: string]: [AdaptorPackageData]
|
|
61
|
+
}> {
|
|
62
|
+
entityMap: [string, T][] = []
|
|
63
|
+
|
|
64
|
+
constructor(remoteChannel: RemoteChannel<T>) {
|
|
65
|
+
super()
|
|
66
|
+
remoteChannel.onServerEvent((e) => {
|
|
67
|
+
const { args } = e
|
|
68
|
+
if (!isRemoteAdaptorData(args)) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
const prevItem = this.entityMap.find(
|
|
72
|
+
(item) => item[1].player.userKey === e.entity.player.userKey
|
|
73
|
+
)
|
|
74
|
+
if (prevItem) {
|
|
75
|
+
prevItem[0] = args.deviceId
|
|
76
|
+
prevItem[1] = e.entity
|
|
77
|
+
} else {
|
|
78
|
+
this.entityMap.push([args.deviceId, e.entity])
|
|
79
|
+
}
|
|
80
|
+
if (!isRemoteValueEvent(args.name)) {
|
|
81
|
+
this.emit('__remote_every__', args)
|
|
82
|
+
}
|
|
83
|
+
this.emit(args.name, args)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onPlayerLeave(e: { entity: T }) {
|
|
88
|
+
this.entityMap = this.entityMap.filter(
|
|
89
|
+
(item) => item[1].player.userKey !== e.entity.player.userKey
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
onEvery(fn: (args: AdaptorPackageData) => void): void {
|
|
94
|
+
this.on('__remote_every__', fn)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
waitForRegister(entity: T) {
|
|
98
|
+
return new Promise<void>((resolve) => {
|
|
99
|
+
if (
|
|
100
|
+
this.entityMap.some(
|
|
101
|
+
([_, en]) => en.player.userKey === entity.player.userKey
|
|
102
|
+
)
|
|
103
|
+
) {
|
|
104
|
+
resolve()
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
this.once('__remote_every__', () => {
|
|
108
|
+
resolve()
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getEntity(deviceId: string): T | undefined {
|
|
114
|
+
return this.entityMap.find((item) => item[0] === deviceId)?.[1]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getIdByEntity(entity: T): string | undefined {
|
|
118
|
+
return this.entityMap.find(
|
|
119
|
+
(item) => item[1].player.userKey === entity.player.userKey
|
|
120
|
+
)?.[0]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
remoteTo(config: { target: T }): {
|
|
124
|
+
targetDeviceId: string
|
|
125
|
+
}
|
|
126
|
+
remoteTo(config: { target: T; timeoutMs: number }): {
|
|
127
|
+
targetDeviceId: string
|
|
128
|
+
timeoutMs: number
|
|
129
|
+
}
|
|
130
|
+
remoteTo({ target, ...rest }: { target: T; timeoutMs?: number }) {
|
|
131
|
+
return {
|
|
132
|
+
targetDeviceId: this.getIdByEntity(target),
|
|
133
|
+
...rest,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
package/src/adaptors/http.ts
CHANGED
|
@@ -21,6 +21,7 @@ export function createHttpAdaptor({
|
|
|
21
21
|
}) {
|
|
22
22
|
const adaptor: Adaptor = {
|
|
23
23
|
every: remoteEventManager.onEvery.bind(remoteEventManager),
|
|
24
|
+
on: remoteEventManager.on.bind(remoteEventManager),
|
|
24
25
|
once: remoteEventManager.once.bind(remoteEventManager),
|
|
25
26
|
off: remoteEventManager.off.bind(remoteEventManager),
|
|
26
27
|
emit: onEmit,
|
package/src/adaptors/iframe.ts
CHANGED
|
@@ -37,6 +37,7 @@ export function createIframeAdaptor({
|
|
|
37
37
|
|
|
38
38
|
const adaptor: Adaptor = {
|
|
39
39
|
every: remoteEventManager.onEvery.bind(remoteEventManager),
|
|
40
|
+
on: remoteEventManager.on.bind(remoteEventManager),
|
|
40
41
|
once: remoteEventManager.once.bind(remoteEventManager),
|
|
41
42
|
off: remoteEventManager.off.bind(remoteEventManager),
|
|
42
43
|
emit: onEmit,
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
export
|
|
1
|
+
export * from './remote'
|
|
2
|
+
export * from './adaptor'
|
|
3
|
+
export * from './response'
|
|
4
|
+
|
|
5
|
+
export * from './remoteValue/remoteValue'
|
|
6
|
+
export * from './remoteValue/exposeToRemote'
|
|
7
|
+
export type { ToFunc, RemoteCallData } from './remoteValue/type'
|
|
8
|
+
|
|
9
|
+
export * from './adaptors/iframe'
|
|
10
|
+
export * from './adaptors/http'
|
|
11
|
+
export * from './adaptors/dao3/client'
|
|
12
|
+
export * from './adaptors/dao3/server'
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Adaptor, AdaptorPackageData } from '../adaptor'
|
|
2
|
+
import { RemoteError, response } from '../response'
|
|
3
|
+
import { RemoteCallData } from './type'
|
|
4
|
+
|
|
5
|
+
type AdaptorData = AdaptorPackageData<RemoteCallData>
|
|
6
|
+
|
|
7
|
+
interface ExposeProps {
|
|
8
|
+
globalName: string
|
|
9
|
+
adaptor: Adaptor
|
|
10
|
+
/**
|
|
11
|
+
* ['*'] or ['device_id_1', 'device_id_2']
|
|
12
|
+
*/
|
|
13
|
+
exposeTo: string[]
|
|
14
|
+
/**
|
|
15
|
+
* 你可以在该回调中抛错,以阻止远程调用,
|
|
16
|
+
* 也可以修改传入的 data
|
|
17
|
+
*/
|
|
18
|
+
onRequest?: (e: AdaptorData) => RemoteCallData | Promise<RemoteCallData>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function defaultOnRequest(e: AdaptorData) {
|
|
22
|
+
return e.data
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isRemoteValueEvent(eventName: string) {
|
|
26
|
+
return eventName.startsWith('__REMOTE_VALUE_REQ__')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function exposeToRemote<T extends object>(obj: T, options: ExposeProps) {
|
|
30
|
+
const {
|
|
31
|
+
globalName,
|
|
32
|
+
adaptor,
|
|
33
|
+
exposeTo,
|
|
34
|
+
onRequest = defaultOnRequest,
|
|
35
|
+
} = options
|
|
36
|
+
const callback = async (e: AdaptorPackageData) => {
|
|
37
|
+
try {
|
|
38
|
+
const { paths, args } = await onRequest(e as AdaptorData)
|
|
39
|
+
if (!exposeTo.includes(e.deviceId) && !exposeTo.includes('*')) {
|
|
40
|
+
throw new RemoteError('permission denied')
|
|
41
|
+
}
|
|
42
|
+
let target = obj
|
|
43
|
+
for (let i = 0; i < paths.length; i += 1) {
|
|
44
|
+
target = target[paths[i] as keyof typeof target] as typeof target
|
|
45
|
+
}
|
|
46
|
+
let res: unknown
|
|
47
|
+
if (target instanceof Function) {
|
|
48
|
+
res = await target(...args)
|
|
49
|
+
} else {
|
|
50
|
+
res = target
|
|
51
|
+
}
|
|
52
|
+
adaptor.emit({
|
|
53
|
+
name: e.callbackName ?? 'IMPOSSIBLE_NO_CALLBACK_NAME',
|
|
54
|
+
deviceId: e.targetDeviceId,
|
|
55
|
+
targetDeviceId: e.deviceId,
|
|
56
|
+
data: response.success(res),
|
|
57
|
+
})
|
|
58
|
+
} catch (error) {
|
|
59
|
+
adaptor.emit({
|
|
60
|
+
name: e.callbackName ?? 'IMPOSSIBLE_NO_CALLBACK_NAME',
|
|
61
|
+
deviceId: e.targetDeviceId,
|
|
62
|
+
targetDeviceId: e.deviceId,
|
|
63
|
+
data: response.error(RemoteError.fromError(error)),
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
adaptor.on(`__REMOTE_VALUE_REQ__${globalName}`, callback)
|
|
68
|
+
return () => {
|
|
69
|
+
adaptor.off(`__REMOTE_VALUE_REQ__${globalName}`, callback)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { RemoteError, RemoteTimeoutError, response } from '../response'
|
|
2
|
+
import { Adaptor, AdaptorCallback, AdaptorPackageData } from '../adaptor'
|
|
3
|
+
import type { RemoteCallData, ToFunc } from './type'
|
|
4
|
+
|
|
5
|
+
function noop() {
|
|
6
|
+
// pass
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type RemoteValueProps = Pick<
|
|
10
|
+
AdaptorPackageData,
|
|
11
|
+
'deviceId' | 'targetDeviceId'
|
|
12
|
+
> & {
|
|
13
|
+
globalName: string
|
|
14
|
+
adaptor: Adaptor
|
|
15
|
+
timeoutMs?: number
|
|
16
|
+
log?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function geneProxy<T extends object>(paths: string[], props: RemoteValueProps) {
|
|
20
|
+
return new Proxy<ToFunc<T>>(noop as unknown as ToFunc<T>, {
|
|
21
|
+
apply(target, thisArg, argArray) {
|
|
22
|
+
const {
|
|
23
|
+
adaptor,
|
|
24
|
+
globalName,
|
|
25
|
+
timeoutMs = 30000,
|
|
26
|
+
log = false,
|
|
27
|
+
...restProps
|
|
28
|
+
} = props
|
|
29
|
+
const randomStr = Math.random().toString(36).slice(2)
|
|
30
|
+
const name = `__REMOTE_VALUE_REQ__${globalName}`
|
|
31
|
+
const responseName = `__REMOTE_VALUE_RES__${[globalName, ...paths].join('.')}-${randomStr}`
|
|
32
|
+
|
|
33
|
+
if (log) {
|
|
34
|
+
console.log(
|
|
35
|
+
`[remoteValue] [${restProps.deviceId}] 正在访问远端 [${restProps.targetDeviceId}] 的变量: "${[globalName, ...paths].join('.')}"`
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
let timer: NodeJS.Timeout | undefined
|
|
41
|
+
const callback: AdaptorCallback = (e) => {
|
|
42
|
+
clearTimeout(timer)
|
|
43
|
+
if (RemoteError.isRemoteError(e.data)) {
|
|
44
|
+
reject(RemoteError.fromError(e.data))
|
|
45
|
+
} else {
|
|
46
|
+
resolve((e.data as ReturnType<typeof response.success>)?.data)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
adaptor.once(responseName, callback)
|
|
50
|
+
timer = setTimeout(() => {
|
|
51
|
+
adaptor.off(responseName, callback)
|
|
52
|
+
reject(
|
|
53
|
+
new RemoteTimeoutError(
|
|
54
|
+
`timeout: ${[globalName, ...paths].join('.')}`
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
}, timeoutMs)
|
|
58
|
+
adaptor.emit({
|
|
59
|
+
name,
|
|
60
|
+
callbackName: responseName,
|
|
61
|
+
...restProps,
|
|
62
|
+
data: {
|
|
63
|
+
paths,
|
|
64
|
+
args: argArray,
|
|
65
|
+
} satisfies RemoteCallData,
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
get(target, prop) {
|
|
70
|
+
return geneProxy([...paths, prop as string], props)
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @example
|
|
77
|
+
* ```
|
|
78
|
+
* const obj = remoteValue<{
|
|
79
|
+
* a: number;
|
|
80
|
+
* b: {
|
|
81
|
+
* c: string;
|
|
82
|
+
* };
|
|
83
|
+
* funcD: () => number
|
|
84
|
+
* }>()
|
|
85
|
+
*
|
|
86
|
+
* const val_1 = await obj.a() // number
|
|
87
|
+
* const val_2 = await obj.b() // { c: string }
|
|
88
|
+
* const val_3 = await obj.b.c() // string
|
|
89
|
+
* const val_4 = await obj.funcD() // number
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function remoteValue<T extends object>(props: RemoteValueProps) {
|
|
93
|
+
return geneProxy<T>([], props)
|
|
94
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
type HasFunc<T> = T extends (...args: unknown[]) => unknown
|
|
2
|
+
? true
|
|
3
|
+
: T extends unknown[]
|
|
4
|
+
? { [K in keyof T]: HasFunc<T[K]> }[number] extends false
|
|
5
|
+
? false
|
|
6
|
+
: true
|
|
7
|
+
: T extends object
|
|
8
|
+
? { [K in keyof T]: HasFunc<T[K]> }[keyof T] extends false
|
|
9
|
+
? false
|
|
10
|
+
: true
|
|
11
|
+
: false
|
|
12
|
+
|
|
13
|
+
export type ToFunc<T extends object> = {
|
|
14
|
+
[K in keyof T]: T[K] extends (...args: infer Args) => infer Ret
|
|
15
|
+
? (...args: Args) => Promise<Awaited<Ret>>
|
|
16
|
+
: HasFunc<T[K]> extends false
|
|
17
|
+
? () => Promise<T[K]>
|
|
18
|
+
: T[K] extends object
|
|
19
|
+
? ToFunc<T[K]>
|
|
20
|
+
: never
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RemoteCallData {
|
|
24
|
+
paths: string[]
|
|
25
|
+
args: unknown[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 下面的都是测试代码,用于测试 ToFunc
|
|
29
|
+
|
|
30
|
+
type Assert<T extends true> = T
|
|
31
|
+
|
|
32
|
+
interface TestObj {
|
|
33
|
+
a1: number
|
|
34
|
+
a2: number[]
|
|
35
|
+
a3: [number, string]
|
|
36
|
+
a4: {
|
|
37
|
+
b: number
|
|
38
|
+
c: [string]
|
|
39
|
+
}
|
|
40
|
+
a5: () => Promise<number>
|
|
41
|
+
a6: () => Promise<number[]>
|
|
42
|
+
a7: () => Promise<[number, string]>
|
|
43
|
+
b: {
|
|
44
|
+
b1: number
|
|
45
|
+
b2: number[]
|
|
46
|
+
b3: [number, string]
|
|
47
|
+
b4: () => {
|
|
48
|
+
b: number
|
|
49
|
+
c: [string]
|
|
50
|
+
}
|
|
51
|
+
b5: () => Promise<number>
|
|
52
|
+
b6: () => Promise<number[]>
|
|
53
|
+
b7: () => Promise<[number, string]>
|
|
54
|
+
}
|
|
55
|
+
f: {
|
|
56
|
+
b: number
|
|
57
|
+
c: {
|
|
58
|
+
d: () => number
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type TestA1 = Assert<
|
|
64
|
+
ToFunc<TestObj>['a1'] extends () => Promise<number> ? true : false
|
|
65
|
+
>
|
|
66
|
+
export type TestA2 = Assert<
|
|
67
|
+
ToFunc<TestObj>['a2'] extends () => Promise<number[]> ? true : false
|
|
68
|
+
>
|
|
69
|
+
export type TestA3 = Assert<
|
|
70
|
+
ToFunc<TestObj>['a3'] extends () => Promise<[number, string]> ? true : false
|
|
71
|
+
>
|
|
72
|
+
export type TestA4 = Assert<
|
|
73
|
+
ToFunc<TestObj>['a4'] extends () => Promise<{
|
|
74
|
+
b: number
|
|
75
|
+
c: [string]
|
|
76
|
+
}>
|
|
77
|
+
? true
|
|
78
|
+
: false
|
|
79
|
+
>
|
|
80
|
+
export type TestA5 = Assert<
|
|
81
|
+
ToFunc<TestObj>['a5'] extends () => Promise<number> ? true : false
|
|
82
|
+
>
|
|
83
|
+
export type TestA6 = Assert<
|
|
84
|
+
ToFunc<TestObj>['a6'] extends () => Promise<number[]> ? true : false
|
|
85
|
+
>
|
|
86
|
+
export type TestA7 = Assert<
|
|
87
|
+
ToFunc<TestObj>['a7'] extends () => Promise<[number, string]> ? true : false
|
|
88
|
+
>
|
|
89
|
+
export type TestB1 = Assert<
|
|
90
|
+
ToFunc<TestObj>['b']['b1'] extends () => Promise<number> ? true : false
|
|
91
|
+
>
|
|
92
|
+
export type TestB2 = Assert<
|
|
93
|
+
ToFunc<TestObj>['b']['b2'] extends () => Promise<number[]> ? true : false
|
|
94
|
+
>
|
|
95
|
+
export type TestB3 = Assert<
|
|
96
|
+
ToFunc<TestObj>['b']['b3'] extends () => Promise<[number, string]>
|
|
97
|
+
? true
|
|
98
|
+
: false
|
|
99
|
+
>
|
|
100
|
+
export type TestB4 = Assert<
|
|
101
|
+
ToFunc<TestObj>['b']['b4'] extends () => Promise<{
|
|
102
|
+
b: number
|
|
103
|
+
c: [string]
|
|
104
|
+
}>
|
|
105
|
+
? true
|
|
106
|
+
: false
|
|
107
|
+
>
|
|
108
|
+
export type TestB5 = Assert<
|
|
109
|
+
ToFunc<TestObj>['b']['b5'] extends () => Promise<number> ? true : false
|
|
110
|
+
>
|
|
111
|
+
export type TestB6 = Assert<
|
|
112
|
+
ToFunc<TestObj>['b']['b6'] extends () => Promise<number[]> ? true : false
|
|
113
|
+
>
|
|
114
|
+
export type TestB7 = Assert<
|
|
115
|
+
ToFunc<TestObj>['b']['b7'] extends () => Promise<[number, string]>
|
|
116
|
+
? true
|
|
117
|
+
: false
|
|
118
|
+
>
|
|
119
|
+
export type TestF = Assert<
|
|
120
|
+
ToFunc<TestObj>['f']['b'] extends () => Promise<number> ? true : false
|
|
121
|
+
>
|
|
122
|
+
export type TestF2 = Assert<
|
|
123
|
+
ToFunc<TestObj>['f']['c']['d'] extends () => Promise<number> ? true : false
|
|
124
|
+
>
|