@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 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 {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=adaptor.js.map
@@ -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":"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"}
@@ -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"}
@@ -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 { Remote, isRemoteAdaptorData } from './remote';
2
- export type { AdaptorPackageData, Adaptor } from './adaptor';
3
- export { RemoteError, RemoteNotFoundError, RemoteTimeoutError, response, } from './response';
4
- export { createIframeAdaptor } from './adaptors/iframe';
5
- export { createHttpAdaptor, remoteEventManager } from './adaptors/http';
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 { Remote, isRemoteAdaptorData } from './remote';
2
- export { RemoteError, RemoteNotFoundError, RemoteTimeoutError, response, } from './response';
3
- export { createIframeAdaptor } from './adaptors/iframe';
4
- export { createHttpAdaptor, remoteEventManager } from './adaptors/http';
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,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAEtD,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,GACT,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA"}
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/remoteValue/type.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zimi/remote",
3
3
  "license": "MIT",
4
- "version": "0.2.0",
4
+ "version": "0.2.1-alpha.11",
5
5
  "author": "xiaomingTang",
6
6
  "description": "call remote functions as local",
7
7
  "private": false,
@@ -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: unknown
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
+ }
@@ -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,
@@ -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 { Remote, isRemoteAdaptorData } from './remote'
2
- export type { AdaptorPackageData, Adaptor } from './adaptor'
3
- export {
4
- RemoteError,
5
- RemoteNotFoundError,
6
- RemoteTimeoutError,
7
- response,
8
- } from './response'
9
- export { createIframeAdaptor } from './adaptors/iframe'
10
- export { createHttpAdaptor, remoteEventManager } from './adaptors/http'
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
+ >