-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.ts
123 lines (91 loc) · 3.07 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import '@abraham/reflection';
import WebSocket from 'isomorphic-ws';
const METADATA_KEY = 'rpc:methods';
interface Call {
id: string;
method: string;
params: any[];
}
interface CallResponse {
id: string;
ok: boolean;
value: any;
}
interface DeferredPromise {
resolve: any;
reject: any;
}
class Executable {
private methods?: string[] = Reflect.getMetadata(METADATA_KEY, Object.create(this));
public execute(instance: any, call: Call) {
const cr = {} as CallResponse;
cr.id = call.id;
if (!this.methods || !this.methods.includes(call.method)) {
cr.ok = false;
} else {
cr.value = instance[call.method].apply(instance, call.params);
cr.ok = true;
}
return cr;
}
}
class WSRPC extends Executable {
callbacks = new Map<string, DeferredPromise>();
public invoke(ws: WebSocket, method: string, params: any[]) {
let id = Math.random().toString(36).substring(2) + Date.now().toString(36);
let p = new Promise((resolve, reject) => this.callbacks.set(id, { resolve, reject }));
ws.send(JSON.stringify({ method, params, id }))
return p;
}
public handle(msg: WebSocket.Data, ws: WebSocket, instance: any) {
let pmsg = JSON.parse(msg.toString());
if (pmsg.method) {
let req = pmsg as Call;
ws.send(JSON.stringify(this.execute(instance, req)));
} else {
let rall = pmsg as CallResponse;
let p = this.callbacks.get(rall.id) as DeferredPromise;
if (rall.ok) p.resolve(rall.value);
else p.reject();
this.callbacks.delete(rall.id);
}
}
}
export class Server extends WSRPC {
private wss!: WebSocket.Server;
private client!: WebSocket;
public listen(port: number) {
this.wss = new WebSocket.Server({ port })
this.wss.on('connection', (ws) => {
const instance = Object.create(this);
instance.client = ws;
ws.onmessage = (msg) => { this.handle(msg.data, ws, instance) }
})
}
public call<T>(method: string, ...params: any[]): Promise<T> {
return super.invoke(this.client, method, params) as Promise<T>;
}
}
export class Client extends WSRPC {
private cws!: WebSocket;
public connect(address: string) {
return new Promise((resolve, reject) => {
this.cws = new WebSocket(address);
this.cws.onopen = () => {
this.cws.onmessage = (msg) => { this.handle(msg.data, this.cws, this) }
resolve();
};
this.cws.onerror = function (err) {
reject(err);
};
});
}
public call<T>(method: string, ...params: any[]): Promise<T> {
return super.invoke(this.cws, method, params) as Promise<T>;
}
}
export function remote(target: any, key: string) {
const methods: string[] = Reflect.getOwnMetadata(METADATA_KEY, target) || [];
methods.push(key);
Reflect.defineMetadata(METADATA_KEY, methods, target);
}