@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.
@@ -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 {};
@@ -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
+ }
@@ -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
+ }