@zimi/remote 0.2.0 → 0.2.1-alpha.3

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
 
@@ -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
@@ -3,3 +3,5 @@ export type { AdaptorPackageData, Adaptor } from './adaptor';
3
3
  export { RemoteError, RemoteNotFoundError, RemoteTimeoutError, response, } from './response';
4
4
  export { createIframeAdaptor } from './adaptors/iframe';
5
5
  export { createHttpAdaptor, remoteEventManager } from './adaptors/http';
6
+ export { remoteValue } from './remoteValue/remoteValue';
7
+ export { exposeToRemote } from './remoteValue/exposeToRemote';
package/dist/index.js CHANGED
@@ -2,4 +2,6 @@ export { Remote, isRemoteAdaptorData } from './remote';
2
2
  export { RemoteError, RemoteNotFoundError, RemoteTimeoutError, response, } from './response';
3
3
  export { createIframeAdaptor } from './adaptors/iframe';
4
4
  export { createHttpAdaptor, remoteEventManager } from './adaptors/http';
5
+ export { remoteValue } from './remoteValue/remoteValue';
6
+ export { exposeToRemote } from './remoteValue/exposeToRemote';
5
7
  //# 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,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;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { Adaptor, AdaptorPackageData } from '../adaptor';
2
+ interface ExposeProps {
3
+ globalName: string;
4
+ adaptor: Adaptor;
5
+ /**
6
+ * ['*'] or ['device_id_1', 'device_id_2']
7
+ */
8
+ exposeTo: string[];
9
+ /**
10
+ * 你可以在该回调中抛错,以阻止远程调用
11
+ */
12
+ onRequest?: (e: AdaptorPackageData) => void | Promise<void>;
13
+ }
14
+ export declare function exposeToRemote<T extends object>(obj: T, options: ExposeProps): () => void;
15
+ export {};
@@ -0,0 +1,47 @@
1
+ import { RemoteError, response } from '../response';
2
+ function defaultOnRequest(e) {
3
+ return e;
4
+ }
5
+ export function exposeToRemote(obj, options) {
6
+ const { globalName, adaptor, exposeTo, onRequest = defaultOnRequest, } = options;
7
+ const callback = async (e) => {
8
+ var _a, _b;
9
+ try {
10
+ await onRequest(e);
11
+ if (!exposeTo.includes(e.deviceId) && !exposeTo.includes('*')) {
12
+ throw new RemoteError('permission denied');
13
+ }
14
+ const { paths, args } = e.data;
15
+ let target = obj;
16
+ for (let i = 0; i < paths.length; i += 1) {
17
+ target = target[paths[i]];
18
+ }
19
+ let res;
20
+ if (target instanceof Function) {
21
+ res = await target(...args);
22
+ }
23
+ else {
24
+ res = target;
25
+ }
26
+ adaptor.emit({
27
+ name: (_a = e.callbackName) !== null && _a !== void 0 ? _a : 'IMPOSSIBLE_NO_CALLBACK_NAME',
28
+ deviceId: e.targetDeviceId,
29
+ targetDeviceId: e.deviceId,
30
+ data: response.success(res),
31
+ });
32
+ }
33
+ catch (error) {
34
+ adaptor.emit({
35
+ name: (_b = e.callbackName) !== null && _b !== void 0 ? _b : 'IMPOSSIBLE_NO_CALLBACK_NAME',
36
+ deviceId: e.targetDeviceId,
37
+ targetDeviceId: e.deviceId,
38
+ data: response.error(RemoteError.fromError(error)),
39
+ });
40
+ }
41
+ };
42
+ adaptor.on(`__REMOTE_VALUE_REQ__${globalName}`, callback);
43
+ return () => {
44
+ adaptor.off(`__REMOTE_VALUE_REQ__${globalName}`, callback);
45
+ };
46
+ }
47
+ //# 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;AAenD,SAAS,gBAAgB,CAAC,CAAqB;IAC7C,OAAO,CAAC,CAAA;AACV,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,SAAS,CAAC,CAAC,CAAC,CAAA;YAClB,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,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,IAA4C,CAAA;YACtE,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,26 @@
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
+ };
8
+ /**
9
+ * @example
10
+ * ```
11
+ * const obj = remoteValue<{
12
+ * a: number;
13
+ * b: {
14
+ * c: string;
15
+ * };
16
+ * funcD: () => number
17
+ * }>()
18
+ *
19
+ * const val_1 = await obj.a() // number
20
+ * const val_2 = await obj.b() // { c: string }
21
+ * const val_3 = await obj.b.c() // string
22
+ * const val_4 = await obj.funcD() // number
23
+ * ```
24
+ */
25
+ export declare function remoteValue<T extends object>(props: RemoteValueProps): ToFunc<T>;
26
+ export {};
@@ -0,0 +1,65 @@
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, ...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
+ return new Promise((resolve, reject) => {
13
+ let timer;
14
+ const callback = (e) => {
15
+ var _a;
16
+ clearTimeout(timer);
17
+ if (RemoteError.isRemoteError(e.data)) {
18
+ reject(RemoteError.fromError(e.data));
19
+ }
20
+ else {
21
+ resolve((_a = e.data) === null || _a === void 0 ? void 0 : _a.data);
22
+ }
23
+ };
24
+ adaptor.once(responseName, callback);
25
+ timer = setTimeout(() => {
26
+ adaptor.off(responseName, callback);
27
+ reject(new RemoteTimeoutError(`timeout: ${[globalName, ...paths].join('.')}`));
28
+ }, timeoutMs);
29
+ adaptor.emit({
30
+ name,
31
+ callbackName: responseName,
32
+ ...restProps,
33
+ data: {
34
+ paths,
35
+ args: argArray,
36
+ },
37
+ });
38
+ });
39
+ },
40
+ get(target, prop) {
41
+ return geneProxy([...paths, prop], props);
42
+ },
43
+ });
44
+ }
45
+ /**
46
+ * @example
47
+ * ```
48
+ * const obj = remoteValue<{
49
+ * a: number;
50
+ * b: {
51
+ * c: string;
52
+ * };
53
+ * funcD: () => number
54
+ * }>()
55
+ *
56
+ * const val_1 = await obj.a() // number
57
+ * const val_2 = await obj.b() // { c: string }
58
+ * const val_3 = await obj.b.c() // string
59
+ * const val_4 = await obj.funcD() // number
60
+ * ```
61
+ */
62
+ export function remoteValue(props) {
63
+ return geneProxy([], props);
64
+ }
65
+ //# 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;AAWD,SAAS,SAAS,CAAmB,KAAe,EAAE,KAAuB;IAC3E,OAAO,IAAI,KAAK,CAAY,IAA4B,EAAE;QACxD,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ;YAC7B,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,CAAA;YACtE,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,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;qBACf;iBACF,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"}
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.3",
5
5
  "author": "xiaomingTang",
6
6
  "description": "call remote functions as local",
7
7
  "private": false,
package/src/adaptor.d.ts CHANGED
@@ -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
  }
@@ -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
@@ -8,3 +8,5 @@ export {
8
8
  } from './response'
9
9
  export { createIframeAdaptor } from './adaptors/iframe'
10
10
  export { createHttpAdaptor, remoteEventManager } from './adaptors/http'
11
+ export { remoteValue } from './remoteValue/remoteValue'
12
+ export { exposeToRemote } from './remoteValue/exposeToRemote'
@@ -0,0 +1,64 @@
1
+ import { Adaptor, AdaptorPackageData } from '../adaptor'
2
+ import { RemoteError, response } from '../response'
3
+
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
+ */
14
+ onRequest?: (e: AdaptorPackageData) => void | Promise<void>
15
+ }
16
+
17
+ function defaultOnRequest(e: AdaptorPackageData) {
18
+ return e
19
+ }
20
+
21
+ export function exposeToRemote<T extends object>(obj: T, options: ExposeProps) {
22
+ const {
23
+ globalName,
24
+ adaptor,
25
+ exposeTo,
26
+ onRequest = defaultOnRequest,
27
+ } = options
28
+ const callback = async (e: AdaptorPackageData) => {
29
+ try {
30
+ await onRequest(e)
31
+ if (!exposeTo.includes(e.deviceId) && !exposeTo.includes('*')) {
32
+ throw new RemoteError('permission denied')
33
+ }
34
+ const { paths, args } = e.data as { paths: string[]; args: unknown[] }
35
+ let target = obj
36
+ for (let i = 0; i < paths.length; i += 1) {
37
+ target = target[paths[i] as keyof typeof target] as typeof target
38
+ }
39
+ let res: unknown
40
+ if (target instanceof Function) {
41
+ res = await target(...args)
42
+ } else {
43
+ res = target
44
+ }
45
+ adaptor.emit({
46
+ name: e.callbackName ?? 'IMPOSSIBLE_NO_CALLBACK_NAME',
47
+ deviceId: e.targetDeviceId,
48
+ targetDeviceId: e.deviceId,
49
+ data: response.success(res),
50
+ })
51
+ } catch (error) {
52
+ adaptor.emit({
53
+ name: e.callbackName ?? 'IMPOSSIBLE_NO_CALLBACK_NAME',
54
+ deviceId: e.targetDeviceId,
55
+ targetDeviceId: e.deviceId,
56
+ data: response.error(RemoteError.fromError(error)),
57
+ })
58
+ }
59
+ }
60
+ adaptor.on(`__REMOTE_VALUE_REQ__${globalName}`, callback)
61
+ return () => {
62
+ adaptor.off(`__REMOTE_VALUE_REQ__${globalName}`, callback)
63
+ }
64
+ }
@@ -0,0 +1,81 @@
1
+ import { RemoteError, RemoteTimeoutError, response } from '../response'
2
+ import { Adaptor, AdaptorCallback, AdaptorPackageData } from '../adaptor'
3
+ import type { 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
+ }
17
+
18
+ function geneProxy<T extends object>(paths: string[], props: RemoteValueProps) {
19
+ return new Proxy<ToFunc<T>>(noop as unknown as ToFunc<T>, {
20
+ apply(target, thisArg, argArray) {
21
+ const { adaptor, globalName, timeoutMs = 30000, ...restProps } = props
22
+ const randomStr = Math.random().toString(36).slice(2)
23
+ const name = `__REMOTE_VALUE_REQ__${globalName}`
24
+ const responseName = `__REMOTE_VALUE_RES__${[globalName, ...paths].join('.')}-${randomStr}`
25
+
26
+ return new Promise((resolve, reject) => {
27
+ let timer: NodeJS.Timeout | undefined
28
+ const callback: AdaptorCallback = (e) => {
29
+ clearTimeout(timer)
30
+ if (RemoteError.isRemoteError(e.data)) {
31
+ reject(RemoteError.fromError(e.data))
32
+ } else {
33
+ resolve((e.data as ReturnType<typeof response.success>)?.data)
34
+ }
35
+ }
36
+ adaptor.once(responseName, callback)
37
+ timer = setTimeout(() => {
38
+ adaptor.off(responseName, callback)
39
+ reject(
40
+ new RemoteTimeoutError(
41
+ `timeout: ${[globalName, ...paths].join('.')}`
42
+ )
43
+ )
44
+ }, timeoutMs)
45
+ adaptor.emit({
46
+ name,
47
+ callbackName: responseName,
48
+ ...restProps,
49
+ data: {
50
+ paths,
51
+ args: argArray,
52
+ },
53
+ })
54
+ })
55
+ },
56
+ get(target, prop) {
57
+ return geneProxy([...paths, prop as string], props)
58
+ },
59
+ })
60
+ }
61
+
62
+ /**
63
+ * @example
64
+ * ```
65
+ * const obj = remoteValue<{
66
+ * a: number;
67
+ * b: {
68
+ * c: string;
69
+ * };
70
+ * funcD: () => number
71
+ * }>()
72
+ *
73
+ * const val_1 = await obj.a() // number
74
+ * const val_2 = await obj.b() // { c: string }
75
+ * const val_3 = await obj.b.c() // string
76
+ * const val_4 = await obj.funcD() // number
77
+ * ```
78
+ */
79
+ export function remoteValue<T extends object>(props: RemoteValueProps) {
80
+ return geneProxy<T>([], props)
81
+ }
@@ -0,0 +1,115 @@
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
+ // 下面的都是测试代码,用于测试 ToFunc
24
+
25
+ type Assert<T extends true> = T
26
+
27
+ interface TestObj {
28
+ a1: number
29
+ a2: number[]
30
+ a3: [number, string]
31
+ a4: {
32
+ b: number
33
+ c: [string]
34
+ }
35
+ a5: () => Promise<number>
36
+ a6: () => Promise<number[]>
37
+ a7: () => Promise<[number, string]>
38
+ b: {
39
+ b1: number
40
+ b2: number[]
41
+ b3: [number, string]
42
+ b4: () => {
43
+ b: number
44
+ c: [string]
45
+ }
46
+ b5: () => Promise<number>
47
+ b6: () => Promise<number[]>
48
+ b7: () => Promise<[number, string]>
49
+ }
50
+ f: {
51
+ b: number
52
+ c: {
53
+ d: () => number
54
+ }
55
+ }
56
+ }
57
+
58
+ export type TestA1 = Assert<
59
+ ToFunc<TestObj>['a1'] extends () => Promise<number> ? true : false
60
+ >
61
+ export type TestA2 = Assert<
62
+ ToFunc<TestObj>['a2'] extends () => Promise<number[]> ? true : false
63
+ >
64
+ export type TestA3 = Assert<
65
+ ToFunc<TestObj>['a3'] extends () => Promise<[number, string]> ? true : false
66
+ >
67
+ export type TestA4 = Assert<
68
+ ToFunc<TestObj>['a4'] extends () => Promise<{
69
+ b: number
70
+ c: [string]
71
+ }>
72
+ ? true
73
+ : false
74
+ >
75
+ export type TestA5 = Assert<
76
+ ToFunc<TestObj>['a5'] extends () => Promise<number> ? true : false
77
+ >
78
+ export type TestA6 = Assert<
79
+ ToFunc<TestObj>['a6'] extends () => Promise<number[]> ? true : false
80
+ >
81
+ export type TestA7 = Assert<
82
+ ToFunc<TestObj>['a7'] extends () => Promise<[number, string]> ? true : false
83
+ >
84
+ export type TestB1 = Assert<
85
+ ToFunc<TestObj>['b']['b1'] extends () => Promise<number> ? true : false
86
+ >
87
+ export type TestB2 = Assert<
88
+ ToFunc<TestObj>['b']['b2'] extends () => Promise<number[]> ? true : false
89
+ >
90
+ export type TestB3 = Assert<
91
+ ToFunc<TestObj>['b']['b3'] extends () => Promise<[number, string]> ? true : false
92
+ >
93
+ export type TestB4 = Assert<
94
+ ToFunc<TestObj>['b']['b4'] extends () => Promise<{
95
+ b: number
96
+ c: [string]
97
+ }>
98
+ ? true
99
+ : false
100
+ >
101
+ export type TestB5 = Assert<
102
+ ToFunc<TestObj>['b']['b5'] extends () => Promise<number> ? true : false
103
+ >
104
+ export type TestB6 = Assert<
105
+ ToFunc<TestObj>['b']['b6'] extends () => Promise<number[]> ? true : false
106
+ >
107
+ export type TestB7 = Assert<
108
+ ToFunc<TestObj>['b']['b7'] extends () => Promise<[number, string]> ? true : false
109
+ >
110
+ export type TestF = Assert<
111
+ ToFunc<TestObj>['f']['b'] extends () => Promise<number> ? true : false
112
+ >
113
+ export type TestF2 = Assert<
114
+ ToFunc<TestObj>['f']['c']['d'] extends () => Promise<number> ? true : false
115
+ >