@zimi/remote 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +184 -0
- package/dist/adaptors/http.d.ts +13 -0
- package/dist/adaptors/http.js +26 -0
- package/dist/adaptors/http.js.map +1 -0
- package/dist/adaptors/iframe.d.ts +4 -0
- package/dist/adaptors/iframe.js +38 -0
- package/dist/adaptors/iframe.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/remote.d.ts +79 -0
- package/dist/remote.js +192 -0
- package/dist/remote.js.map +1 -0
- package/dist/response.d.ts +104 -0
- package/dist/response.js +155 -0
- package/dist/response.js.map +1 -0
- package/package.json +24 -0
- package/src/adaptor.d.ts +33 -0
- package/src/adaptors/http.ts +30 -0
- package/src/adaptors/iframe.ts +46 -0
- package/src/index.ts +10 -0
- package/src/remote.ts +257 -0
- package/src/response.ts +170 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
interface RawRemoteError {
|
|
2
|
+
/**
|
|
3
|
+
* code > 0 才是合法的 Error;
|
|
4
|
+
* name 和 message 都是 human readable 的字符串,
|
|
5
|
+
* 仅用于调试和展示,
|
|
6
|
+
* 程序逻辑判断请使用 code
|
|
7
|
+
*/
|
|
8
|
+
code: number;
|
|
9
|
+
/**
|
|
10
|
+
* name 和 message 都是 human readable 的字符串,
|
|
11
|
+
* 仅用于调试和展示,
|
|
12
|
+
* 程序逻辑判断请使用 code
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
/**
|
|
16
|
+
* name 和 message 都是 human readable 的字符串,
|
|
17
|
+
* 仅用于调试和展示,
|
|
18
|
+
* 程序逻辑判断请使用 code
|
|
19
|
+
*/
|
|
20
|
+
message: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* name 和 message 都是 human readable 的字符串,
|
|
24
|
+
* 仅用于调试和展示,
|
|
25
|
+
* 程序逻辑判断请使用 code
|
|
26
|
+
*/
|
|
27
|
+
export declare class RemoteError extends Error {
|
|
28
|
+
/**
|
|
29
|
+
* code > 0 才是合法的 Error;
|
|
30
|
+
* name 和 message 都是 human readable 的字符串,
|
|
31
|
+
* 仅用于调试和展示,
|
|
32
|
+
* 程序逻辑判断请使用 code
|
|
33
|
+
*/
|
|
34
|
+
code: number;
|
|
35
|
+
/**
|
|
36
|
+
* name 和 message 都是 human readable 的字符串,
|
|
37
|
+
* 仅用于调试和展示,
|
|
38
|
+
* 程序逻辑判断请使用 code
|
|
39
|
+
*/
|
|
40
|
+
constructor(message: string);
|
|
41
|
+
toJson(): RawRemoteError;
|
|
42
|
+
toString(): string;
|
|
43
|
+
valueOf(): string;
|
|
44
|
+
static fromError(err: unknown): RemoteError;
|
|
45
|
+
static isRemoteError(data: unknown): data is RawRemoteError;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* name 和 message 都是 human readable 的字符串,
|
|
49
|
+
* 仅用于调试和展示,
|
|
50
|
+
* 程序逻辑判断请使用 code
|
|
51
|
+
*/
|
|
52
|
+
export declare class RemoteTimeoutError extends RemoteError {
|
|
53
|
+
/**
|
|
54
|
+
* name 和 message 都是 human readable 的字符串,
|
|
55
|
+
* 仅用于调试和展示,
|
|
56
|
+
* 程序逻辑判断请使用 code
|
|
57
|
+
*/
|
|
58
|
+
static code: number;
|
|
59
|
+
/**
|
|
60
|
+
* name 和 message 都是 human readable 的字符串,
|
|
61
|
+
* 仅用于调试和展示,
|
|
62
|
+
* 程序逻辑判断请使用 code
|
|
63
|
+
*/
|
|
64
|
+
code: number;
|
|
65
|
+
/**
|
|
66
|
+
* name 和 message 都是 human readable 的字符串,
|
|
67
|
+
* 仅用于调试和展示,
|
|
68
|
+
* 程序逻辑判断请使用 code
|
|
69
|
+
*/
|
|
70
|
+
constructor(message: string);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* name 和 message 都是 human readable 的字符串,
|
|
74
|
+
* 仅用于调试和展示,
|
|
75
|
+
* 程序逻辑判断请使用 code
|
|
76
|
+
*/
|
|
77
|
+
export declare class RemoteNotFoundError extends RemoteError {
|
|
78
|
+
/**
|
|
79
|
+
* name 和 message 都是 human readable 的字符串,
|
|
80
|
+
* 仅用于调试和展示,
|
|
81
|
+
* 程序逻辑判断请使用 code
|
|
82
|
+
*/
|
|
83
|
+
static code: number;
|
|
84
|
+
/**
|
|
85
|
+
* name 和 message 都是 human readable 的字符串,
|
|
86
|
+
* 仅用于调试和展示,
|
|
87
|
+
* 程序逻辑判断请使用 code
|
|
88
|
+
*/
|
|
89
|
+
code: number;
|
|
90
|
+
/**
|
|
91
|
+
* name 和 message 都是 human readable 的字符串,
|
|
92
|
+
* 仅用于调试和展示,
|
|
93
|
+
* 程序逻辑判断请使用 code
|
|
94
|
+
*/
|
|
95
|
+
constructor(message: string);
|
|
96
|
+
}
|
|
97
|
+
export declare const response: {
|
|
98
|
+
success<T>(data: T): {
|
|
99
|
+
readonly code: 0;
|
|
100
|
+
readonly data: T;
|
|
101
|
+
};
|
|
102
|
+
error(error: RemoteError): RawRemoteError;
|
|
103
|
+
};
|
|
104
|
+
export {};
|
package/dist/response.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* name 和 message 都是 human readable 的字符串,
|
|
3
|
+
* 仅用于调试和展示,
|
|
4
|
+
* 程序逻辑判断请使用 code
|
|
5
|
+
*/
|
|
6
|
+
export class RemoteError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* name 和 message 都是 human readable 的字符串,
|
|
9
|
+
* 仅用于调试和展示,
|
|
10
|
+
* 程序逻辑判断请使用 code
|
|
11
|
+
*/
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
/**
|
|
15
|
+
* code > 0 才是合法的 Error;
|
|
16
|
+
* name 和 message 都是 human readable 的字符串,
|
|
17
|
+
* 仅用于调试和展示,
|
|
18
|
+
* 程序逻辑判断请使用 code
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(this, "code", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: 1
|
|
25
|
+
});
|
|
26
|
+
this.name = 'RemoteError';
|
|
27
|
+
}
|
|
28
|
+
toJson() {
|
|
29
|
+
return {
|
|
30
|
+
code: this.code,
|
|
31
|
+
name: this.name,
|
|
32
|
+
message: this.message,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
toString() {
|
|
36
|
+
return JSON.stringify(this.toJson());
|
|
37
|
+
}
|
|
38
|
+
valueOf() {
|
|
39
|
+
return `${this.name} [${this.code}]: ${this.message}`;
|
|
40
|
+
}
|
|
41
|
+
static fromError(err) {
|
|
42
|
+
if (typeof err === 'string') {
|
|
43
|
+
return new RemoteError(err);
|
|
44
|
+
}
|
|
45
|
+
if (!err) {
|
|
46
|
+
return new RemoteError('Unknown error');
|
|
47
|
+
}
|
|
48
|
+
const json = err;
|
|
49
|
+
const error = new RemoteError(typeof json.message === 'string' ? json.message : 'Unknown error');
|
|
50
|
+
if (typeof json.code === 'number' && json.code > 0) {
|
|
51
|
+
error.code = json.code;
|
|
52
|
+
}
|
|
53
|
+
if (json.name && typeof json.name === 'string') {
|
|
54
|
+
error.name = json.name;
|
|
55
|
+
}
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
static isRemoteError(data) {
|
|
59
|
+
const json = data;
|
|
60
|
+
return (!!json &&
|
|
61
|
+
typeof json === 'object' &&
|
|
62
|
+
typeof json.code === 'number' &&
|
|
63
|
+
json.code > 0 &&
|
|
64
|
+
typeof json.name === 'string' &&
|
|
65
|
+
typeof json.message === 'string');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* name 和 message 都是 human readable 的字符串,
|
|
70
|
+
* 仅用于调试和展示,
|
|
71
|
+
* 程序逻辑判断请使用 code
|
|
72
|
+
*/
|
|
73
|
+
export class RemoteTimeoutError extends RemoteError {
|
|
74
|
+
/**
|
|
75
|
+
* name 和 message 都是 human readable 的字符串,
|
|
76
|
+
* 仅用于调试和展示,
|
|
77
|
+
* 程序逻辑判断请使用 code
|
|
78
|
+
*/
|
|
79
|
+
constructor(message) {
|
|
80
|
+
super(message);
|
|
81
|
+
/**
|
|
82
|
+
* name 和 message 都是 human readable 的字符串,
|
|
83
|
+
* 仅用于调试和展示,
|
|
84
|
+
* 程序逻辑判断请使用 code
|
|
85
|
+
*/
|
|
86
|
+
Object.defineProperty(this, "code", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
writable: true,
|
|
90
|
+
value: 2
|
|
91
|
+
});
|
|
92
|
+
this.name = 'RemoteTimeoutError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* name 和 message 都是 human readable 的字符串,
|
|
97
|
+
* 仅用于调试和展示,
|
|
98
|
+
* 程序逻辑判断请使用 code
|
|
99
|
+
*/
|
|
100
|
+
Object.defineProperty(RemoteTimeoutError, "code", {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
configurable: true,
|
|
103
|
+
writable: true,
|
|
104
|
+
value: 2
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* name 和 message 都是 human readable 的字符串,
|
|
108
|
+
* 仅用于调试和展示,
|
|
109
|
+
* 程序逻辑判断请使用 code
|
|
110
|
+
*/
|
|
111
|
+
export class RemoteNotFoundError extends RemoteError {
|
|
112
|
+
/**
|
|
113
|
+
* name 和 message 都是 human readable 的字符串,
|
|
114
|
+
* 仅用于调试和展示,
|
|
115
|
+
* 程序逻辑判断请使用 code
|
|
116
|
+
*/
|
|
117
|
+
constructor(message) {
|
|
118
|
+
super(message);
|
|
119
|
+
/**
|
|
120
|
+
* name 和 message 都是 human readable 的字符串,
|
|
121
|
+
* 仅用于调试和展示,
|
|
122
|
+
* 程序逻辑判断请使用 code
|
|
123
|
+
*/
|
|
124
|
+
Object.defineProperty(this, "code", {
|
|
125
|
+
enumerable: true,
|
|
126
|
+
configurable: true,
|
|
127
|
+
writable: true,
|
|
128
|
+
value: 3
|
|
129
|
+
});
|
|
130
|
+
this.name = 'RemoteNotFoundError';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* name 和 message 都是 human readable 的字符串,
|
|
135
|
+
* 仅用于调试和展示,
|
|
136
|
+
* 程序逻辑判断请使用 code
|
|
137
|
+
*/
|
|
138
|
+
Object.defineProperty(RemoteNotFoundError, "code", {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
configurable: true,
|
|
141
|
+
writable: true,
|
|
142
|
+
value: 3
|
|
143
|
+
});
|
|
144
|
+
export const response = {
|
|
145
|
+
success(data) {
|
|
146
|
+
return {
|
|
147
|
+
code: 0,
|
|
148
|
+
data,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
error(error) {
|
|
152
|
+
return error.toJson();
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zimi/remote",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"author": "xiaomingTang",
|
|
6
|
+
"description": "call remote functions as local",
|
|
7
|
+
"private": false,
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"module": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"repository": "https://github.com/xiaomingTang/xiaoming/tree/master/%40zimi/remote",
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"build:watch": "tsc --watch"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"eventemitter3": "^5.0.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/adaptor.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface AdaptorPackageData {
|
|
2
|
+
/**
|
|
3
|
+
* 自身设备 id,应确保唯一性(对方能凭借该 deviceId 找到该设备)
|
|
4
|
+
*/
|
|
5
|
+
deviceId: string
|
|
6
|
+
/**
|
|
7
|
+
* 对方的设备 id
|
|
8
|
+
*/
|
|
9
|
+
targetDeviceId: string
|
|
10
|
+
/**
|
|
11
|
+
* 远程调用的对方的方法名
|
|
12
|
+
*/
|
|
13
|
+
name: string
|
|
14
|
+
data: unknown
|
|
15
|
+
/**
|
|
16
|
+
* 所需回调的方法名(如果需要回调的话)
|
|
17
|
+
*/
|
|
18
|
+
callbackName?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type Func<D = unknown, R = unknown> = (data: D) => R
|
|
22
|
+
|
|
23
|
+
type AdaptorCallback = Func<AdaptorPackageData, void>
|
|
24
|
+
|
|
25
|
+
export interface Adaptor {
|
|
26
|
+
every: (callback: AdaptorCallback) => void
|
|
27
|
+
/**
|
|
28
|
+
* off 用于移除 once 注册的事件,当事件超时后,需要主动 off
|
|
29
|
+
*/
|
|
30
|
+
off: (name: string, callback: AdaptorCallback) => void
|
|
31
|
+
once: (name: string, callback: AdaptorCallback) => void
|
|
32
|
+
emit: (data: AdaptorPackageData) => void
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3'
|
|
2
|
+
|
|
3
|
+
import type { Adaptor, AdaptorPackageData } from '../adaptor'
|
|
4
|
+
|
|
5
|
+
class RemoteEventManager extends EventEmitter<{
|
|
6
|
+
[key: string]: [AdaptorPackageData]
|
|
7
|
+
}> {
|
|
8
|
+
EVERY_EVENT_NAME = '__remote_every__'
|
|
9
|
+
|
|
10
|
+
onEvery(fn: (data: AdaptorPackageData) => void) {
|
|
11
|
+
this.on(this.EVERY_EVENT_NAME, fn)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const remoteEventManager = new RemoteEventManager()
|
|
16
|
+
|
|
17
|
+
export function createHttpAdaptor({
|
|
18
|
+
onEmit,
|
|
19
|
+
}: {
|
|
20
|
+
onEmit: (data: AdaptorPackageData) => void
|
|
21
|
+
}) {
|
|
22
|
+
const adaptor: Adaptor = {
|
|
23
|
+
every: remoteEventManager.onEvery.bind(remoteEventManager),
|
|
24
|
+
once: remoteEventManager.once.bind(remoteEventManager),
|
|
25
|
+
off: remoteEventManager.off.bind(remoteEventManager),
|
|
26
|
+
emit: onEmit,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return adaptor
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import EventEmitter from 'eventemitter3'
|
|
2
|
+
import { isRemoteAdaptorData } from '../remote'
|
|
3
|
+
|
|
4
|
+
import type { Adaptor, AdaptorPackageData } from '../adaptor'
|
|
5
|
+
|
|
6
|
+
class RemoteEventManager extends EventEmitter<{
|
|
7
|
+
[key: string]: [AdaptorPackageData]
|
|
8
|
+
}> {
|
|
9
|
+
EVERY_EVENT_NAME = '__remote_every__'
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
super()
|
|
13
|
+
if (typeof window === 'undefined') {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
window.addEventListener('message', (event) => {
|
|
17
|
+
const { data } = event
|
|
18
|
+
if (isRemoteAdaptorData(data)) {
|
|
19
|
+
this.emit(data.name, data)
|
|
20
|
+
// 一定要抛出 every 事件,remote 包基于此处理远端的响应
|
|
21
|
+
this.emit(this.EVERY_EVENT_NAME, data)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onEvery(fn: (data: AdaptorPackageData) => void) {
|
|
27
|
+
this.on(this.EVERY_EVENT_NAME, fn)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createIframeAdaptor({
|
|
32
|
+
onEmit,
|
|
33
|
+
}: {
|
|
34
|
+
onEmit: (data: AdaptorPackageData) => void
|
|
35
|
+
}) {
|
|
36
|
+
const remoteEventManager = new RemoteEventManager()
|
|
37
|
+
|
|
38
|
+
const adaptor: Adaptor = {
|
|
39
|
+
every: remoteEventManager.onEvery.bind(remoteEventManager),
|
|
40
|
+
once: remoteEventManager.once.bind(remoteEventManager),
|
|
41
|
+
off: remoteEventManager.off.bind(remoteEventManager),
|
|
42
|
+
emit: onEmit,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return adaptor
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
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'
|
package/src/remote.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RemoteError,
|
|
3
|
+
RemoteNotFoundError,
|
|
4
|
+
RemoteTimeoutError,
|
|
5
|
+
response,
|
|
6
|
+
} from './response'
|
|
7
|
+
|
|
8
|
+
import type { Adaptor, AdaptorCallback, AdaptorPackageData } from './adaptor'
|
|
9
|
+
|
|
10
|
+
const RESPONSE_PREFIX = '_response_'
|
|
11
|
+
|
|
12
|
+
type LogFunc = (...data: unknown[]) => void
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
type RemoteCallableFunc = (data: any) => Promise<any>
|
|
16
|
+
|
|
17
|
+
interface RemoteFuncRecords {
|
|
18
|
+
[key: string]: RemoteCallableFunc
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type FuncMapWithConfig<T extends RemoteFuncRecords> = {
|
|
22
|
+
[K in keyof T]: T[K] extends (data: infer Arg) => Promise<infer Ret>
|
|
23
|
+
? (
|
|
24
|
+
data: Arg,
|
|
25
|
+
config?: {
|
|
26
|
+
timeoutMs?: number
|
|
27
|
+
targetDeviceId?: string
|
|
28
|
+
}
|
|
29
|
+
) => Promise<Ret>
|
|
30
|
+
: never
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type RegisteredFunc<T extends RemoteCallableFunc> = T extends (
|
|
34
|
+
data: infer Arg
|
|
35
|
+
) => infer Ret
|
|
36
|
+
? (
|
|
37
|
+
data: Arg,
|
|
38
|
+
ctx: {
|
|
39
|
+
/**
|
|
40
|
+
* 对方的设备 id
|
|
41
|
+
*/
|
|
42
|
+
deviceId: string
|
|
43
|
+
}
|
|
44
|
+
) => Ret
|
|
45
|
+
: never
|
|
46
|
+
|
|
47
|
+
function defaultLog(
|
|
48
|
+
this: Remote<
|
|
49
|
+
{
|
|
50
|
+
[key: string]: RemoteCallableFunc
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
[key: string]: RemoteCallableFunc
|
|
54
|
+
}
|
|
55
|
+
>,
|
|
56
|
+
...data: unknown[]
|
|
57
|
+
) {
|
|
58
|
+
if (!this.debug) {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
console.log(`[remote of ${this.deviceId}]`, ...data)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class Remote<
|
|
65
|
+
/**
|
|
66
|
+
* MF means my functions
|
|
67
|
+
*/
|
|
68
|
+
MF extends RemoteFuncRecords,
|
|
69
|
+
/**
|
|
70
|
+
* OF means others functions
|
|
71
|
+
*/
|
|
72
|
+
OF extends RemoteFuncRecords,
|
|
73
|
+
> {
|
|
74
|
+
debug = false
|
|
75
|
+
|
|
76
|
+
private log: LogFunc
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* (调用对方函数时的)默认超时时间,单位 ms
|
|
80
|
+
* @default 30000
|
|
81
|
+
*/
|
|
82
|
+
private defaultTimeoutMs = 30000
|
|
83
|
+
|
|
84
|
+
private map: {
|
|
85
|
+
[key: string]: {
|
|
86
|
+
callback: RegisteredFunc<RemoteCallableFunc>
|
|
87
|
+
}
|
|
88
|
+
} = {}
|
|
89
|
+
|
|
90
|
+
private deviceIdValue = ''
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 设备 id 应该唯一,用于区分不同设备。
|
|
94
|
+
* 你可以在任何时候修改(更新)它。
|
|
95
|
+
* @default ''
|
|
96
|
+
*/
|
|
97
|
+
get deviceId() {
|
|
98
|
+
return this.deviceIdValue
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
set deviceId(deviceId: string) {
|
|
102
|
+
this.log(`deviceId set: from "${this.deviceIdValue}" to "${deviceId}"`)
|
|
103
|
+
this.deviceIdValue = deviceId
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
private adaptor: Adaptor,
|
|
108
|
+
config?: {
|
|
109
|
+
/**
|
|
110
|
+
* 设备 id 应该唯一,用于区分不同设备。
|
|
111
|
+
* 你可以在任何时候修改(更新)它。
|
|
112
|
+
* @default ''
|
|
113
|
+
*/
|
|
114
|
+
deviceId?: string
|
|
115
|
+
/**
|
|
116
|
+
* (调用对方函数时的)默认超时时间,单位 ms
|
|
117
|
+
* @default 30000
|
|
118
|
+
*/
|
|
119
|
+
defaultTimeoutMs?: number
|
|
120
|
+
debug?: boolean
|
|
121
|
+
/**
|
|
122
|
+
* 格式化 AdaptorPackageData 的函数,
|
|
123
|
+
* 用于调试时输出日志。
|
|
124
|
+
* @default JSON.stringify
|
|
125
|
+
*/
|
|
126
|
+
log?: LogFunc
|
|
127
|
+
}
|
|
128
|
+
) {
|
|
129
|
+
this.debug = config?.debug ?? this.debug
|
|
130
|
+
this.defaultTimeoutMs = config?.defaultTimeoutMs ?? this.defaultTimeoutMs
|
|
131
|
+
this.log = config?.log ?? defaultLog.bind(this)
|
|
132
|
+
this.deviceId = config?.deviceId ?? this.deviceId
|
|
133
|
+
|
|
134
|
+
adaptor.every(async (e) => {
|
|
135
|
+
const { deviceId: selfDeviceId } = this
|
|
136
|
+
const { name, data, deviceId: targetDeviceId, callbackName } = e
|
|
137
|
+
const callback = this.map[name]?.callback
|
|
138
|
+
if (!callback) {
|
|
139
|
+
if (name.startsWith(RESPONSE_PREFIX)) {
|
|
140
|
+
// 这是响应,会在 callAsync once 中处理,这儿不用处理
|
|
141
|
+
this.log('[every] response received: ', e)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
this.log('callback not found: ', name)
|
|
145
|
+
if (callbackName) {
|
|
146
|
+
adaptor.emit({
|
|
147
|
+
deviceId: selfDeviceId,
|
|
148
|
+
targetDeviceId,
|
|
149
|
+
name: callbackName,
|
|
150
|
+
data: response.error(
|
|
151
|
+
new RemoteNotFoundError(`callback not found: ${name}`)
|
|
152
|
+
),
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
if (!callbackName) {
|
|
158
|
+
this.log('should not respond: ', e)
|
|
159
|
+
void callback(data, { deviceId: targetDeviceId })
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
this.log(`callback: ${name}; respondName: ${callbackName}; data: `, data)
|
|
163
|
+
try {
|
|
164
|
+
const ret = await callback(data, { deviceId: targetDeviceId })
|
|
165
|
+
this.log('callback return: ', ret)
|
|
166
|
+
adaptor.emit({
|
|
167
|
+
deviceId: selfDeviceId,
|
|
168
|
+
targetDeviceId,
|
|
169
|
+
name: callbackName,
|
|
170
|
+
data: response.success(ret),
|
|
171
|
+
})
|
|
172
|
+
} catch (error) {
|
|
173
|
+
this.log('callback error: ', error)
|
|
174
|
+
adaptor.emit({
|
|
175
|
+
deviceId: selfDeviceId,
|
|
176
|
+
targetDeviceId,
|
|
177
|
+
name: callbackName,
|
|
178
|
+
data: response.error(RemoteError.fromError(error)),
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 调用其他端的方法;会等待对方响应。
|
|
186
|
+
* 不能直接使用该方法,应该使用 proxy。
|
|
187
|
+
* @WARNING 不能用于响应其他端。
|
|
188
|
+
*/
|
|
189
|
+
private callAsync(
|
|
190
|
+
name: string,
|
|
191
|
+
data: unknown,
|
|
192
|
+
config?: {
|
|
193
|
+
timeoutMs?: number
|
|
194
|
+
targetDeviceId?: string
|
|
195
|
+
}
|
|
196
|
+
) {
|
|
197
|
+
const timeoutMs = config?.timeoutMs ?? this.defaultTimeoutMs
|
|
198
|
+
const { deviceId } = this
|
|
199
|
+
const randomStr = Math.random().toString(36).slice(2)
|
|
200
|
+
// 本条消息的响应名
|
|
201
|
+
const responseName = `${RESPONSE_PREFIX}-${name}-${deviceId}-${randomStr}`
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
let timer: NodeJS.Timeout | undefined
|
|
204
|
+
this.log(`callAsync ${name}: waiting for response: ${responseName}`)
|
|
205
|
+
const callback: AdaptorCallback = (e) => {
|
|
206
|
+
clearTimeout(timer)
|
|
207
|
+
this.log(`response received: ${responseName}`, e)
|
|
208
|
+
if (RemoteError.isRemoteError(e.data)) {
|
|
209
|
+
reject(RemoteError.fromError(e.data))
|
|
210
|
+
} else {
|
|
211
|
+
resolve((e.data as ReturnType<typeof response.success>)?.data)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
this.adaptor.once(responseName, callback)
|
|
215
|
+
timer = setTimeout(() => {
|
|
216
|
+
this.log(`timeout: ${responseName}`)
|
|
217
|
+
this.adaptor.off(responseName, callback)
|
|
218
|
+
reject(new RemoteTimeoutError(`timeout: ${name}`))
|
|
219
|
+
}, timeoutMs)
|
|
220
|
+
this.adaptor.emit({
|
|
221
|
+
deviceId,
|
|
222
|
+
targetDeviceId: config?.targetDeviceId ?? '',
|
|
223
|
+
name,
|
|
224
|
+
data,
|
|
225
|
+
callbackName: responseName,
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 注册方法,供对方调用;
|
|
232
|
+
*/
|
|
233
|
+
register<K extends keyof MF>(name: K, callback: RegisteredFunc<MF[K]>): void {
|
|
234
|
+
this.map[name as string] = { callback }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
_ = new Proxy<FuncMapWithConfig<OF>>({} as FuncMapWithConfig<OF>, {
|
|
238
|
+
get: (_, k) => this.callAsync.bind(this, k as string),
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
self = new Proxy<MF>({} as MF, {
|
|
242
|
+
get: (_, k) => this.map[k as string]?.callback,
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function isRemoteAdaptorData(data: unknown): data is AdaptorPackageData {
|
|
247
|
+
return (
|
|
248
|
+
!!data &&
|
|
249
|
+
typeof data === 'object' &&
|
|
250
|
+
'deviceId' in data &&
|
|
251
|
+
'name' in data &&
|
|
252
|
+
typeof data.deviceId === 'string' &&
|
|
253
|
+
typeof data.name === 'string' &&
|
|
254
|
+
!!data.deviceId &&
|
|
255
|
+
!!data.name
|
|
256
|
+
)
|
|
257
|
+
}
|