@mingzey/typedrpc 1.0.1 → 1.0.2
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/package.json +3 -2
- package/src/api.ts +72 -0
- package/src/client.ts +77 -0
- package/src/connecitons/basic.ts +49 -0
- package/src/connecitons/http.ts +184 -0
- package/src/connecitons/socket.ts +247 -0
- package/src/connecitons/socketio.ts +200 -0
- package/src/connection.ts +10 -0
- package/src/context.ts +18 -0
- package/src/core.ts +130 -0
- package/src/define.ts +63 -0
- package/src/handler.ts +202 -0
- package/src/index.ts +41 -0
- package/src/packet.ts +77 -0
- package/src/server.ts +82 -0
- package/src/test/TestCase.ts +6 -0
- package/src/test/authorization.ts +197 -0
- package/src/test/basic.ts +44 -0
- package/src/test/context.ts +64 -0
- package/src/test/expressmix.ts +81 -0
- package/src/test/socket.ts +90 -0
- package/src/test/socketio.ts +96 -0
- package/src/test.ts +35 -0
- package/src/utils.ts +95 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { IdMaker, TypedEmitter } from "../utils.js";
|
|
2
|
+
import { TypedRPCConnection, TypedRPCConnectionProvider } from "./basic.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
type TypedRPCConnectionSocketIOPayload = {
|
|
7
|
+
type:'request' | 'response';
|
|
8
|
+
id:string;
|
|
9
|
+
data:string;
|
|
10
|
+
}
|
|
11
|
+
type TypedRPCConnectionMessageEvents = {
|
|
12
|
+
receive:(data:string) => void;
|
|
13
|
+
}
|
|
14
|
+
class TypedRPCConnectionSocketIO extends TypedRPCConnection{
|
|
15
|
+
public msgEmitter = new TypedEmitter<TypedRPCConnectionMessageEvents>();
|
|
16
|
+
|
|
17
|
+
private currentId = 1;
|
|
18
|
+
|
|
19
|
+
private requests:Map<string,{
|
|
20
|
+
resolve:(data:string) => void
|
|
21
|
+
}> = new Map();
|
|
22
|
+
|
|
23
|
+
constructor(private socket:{
|
|
24
|
+
id:string,
|
|
25
|
+
send:(data:string) => void;
|
|
26
|
+
close:() => boolean;
|
|
27
|
+
isClosed:() => boolean;
|
|
28
|
+
}){
|
|
29
|
+
super();
|
|
30
|
+
|
|
31
|
+
// 处理外部请求
|
|
32
|
+
this.msgEmitter.on('receive',(data) => {
|
|
33
|
+
// console.log(`[${this.socket.id}][R]:${data}`);
|
|
34
|
+
const recivePlayload:TypedRPCConnectionSocketIOPayload = JSON.parse(data);
|
|
35
|
+
if(recivePlayload.type == 'request'
|
|
36
|
+
&& recivePlayload.id
|
|
37
|
+
){
|
|
38
|
+
this.emitter.emit('request',{// 告知TypedRPCHandler有新请求
|
|
39
|
+
data:recivePlayload.data,
|
|
40
|
+
response:(data) => {
|
|
41
|
+
const sendPlayout:TypedRPCConnectionSocketIOPayload = {
|
|
42
|
+
type:'response',
|
|
43
|
+
id:recivePlayload.id,
|
|
44
|
+
data:data,
|
|
45
|
+
}
|
|
46
|
+
this.socket.send(JSON.stringify(sendPlayout));
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}else if(recivePlayload.type == 'response'
|
|
50
|
+
&& recivePlayload.id
|
|
51
|
+
){
|
|
52
|
+
this.requests.get(recivePlayload.id)?.resolve(recivePlayload.data);
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async request(data: string): Promise<string> {
|
|
58
|
+
const requestId = `${this.currentId++}`;
|
|
59
|
+
const payload:TypedRPCConnectionSocketIOPayload = {
|
|
60
|
+
type:'request',
|
|
61
|
+
id:requestId,
|
|
62
|
+
data:data,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const requestPromise = new Promise<string>((resolve) => {
|
|
66
|
+
this.requests.set(requestId,{
|
|
67
|
+
resolve:resolve
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// 发送数据
|
|
71
|
+
// console.log(`[${this.socket.id}][T]:${JSON.stringify(payload)}`);
|
|
72
|
+
this.socket.send(JSON.stringify(payload));
|
|
73
|
+
}).finally(() => {
|
|
74
|
+
this.requests.delete(requestId);
|
|
75
|
+
})
|
|
76
|
+
return await requestPromise;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getId(): string {
|
|
80
|
+
return this.socket.id;
|
|
81
|
+
}
|
|
82
|
+
close(): boolean {
|
|
83
|
+
return this.socket.close();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
isClosed(): boolean {
|
|
87
|
+
return this.socket.isClosed();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class TypedRPCConnectionProviderSocketIO extends TypedRPCConnectionProvider{
|
|
93
|
+
|
|
94
|
+
private io:import("socket.io").Server | null = null;
|
|
95
|
+
|
|
96
|
+
async listen(config: { port: number; hostname?: string; }): Promise<boolean> {
|
|
97
|
+
const httpModule = await import("http").catch(() => null);
|
|
98
|
+
if(!httpModule){
|
|
99
|
+
throw new Error("http module not found");
|
|
100
|
+
}
|
|
101
|
+
const socketIOServerModule = await import("socket.io").catch(() => null);
|
|
102
|
+
if(!socketIOServerModule){
|
|
103
|
+
throw new Error("socket.io module not found");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const server = httpModule.createServer();
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
const io = new socketIOServerModule.Server(server,{
|
|
110
|
+
cors:{
|
|
111
|
+
origin:"*",
|
|
112
|
+
methods:["GET","POST"],
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
io.on('connection',(socket) => {
|
|
116
|
+
const connection = new TypedRPCConnectionSocketIO({
|
|
117
|
+
id:socket.id,
|
|
118
|
+
send:(data) => {
|
|
119
|
+
socket.emit('message',data);
|
|
120
|
+
},
|
|
121
|
+
close:() => {
|
|
122
|
+
socket.disconnect();
|
|
123
|
+
return true;
|
|
124
|
+
},
|
|
125
|
+
isClosed:() => {
|
|
126
|
+
return socket.disconnected;
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
this.emitter.emit('connection',connection);// 告知TypedRPCHandler有新连接
|
|
130
|
+
socket.on('message',(data) => {
|
|
131
|
+
connection.msgEmitter.emit('receive',data);
|
|
132
|
+
})
|
|
133
|
+
socket.on('close',() => {
|
|
134
|
+
connection.close();
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
this.io = io;
|
|
138
|
+
return new Promise<boolean>((resolve) => {
|
|
139
|
+
server.listen(config.port,config.hostname,() => {
|
|
140
|
+
resolve(true);
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
async close(): Promise<boolean> {
|
|
145
|
+
return new Promise<boolean>((resolve,reject) => {
|
|
146
|
+
if(!this.io){
|
|
147
|
+
return resolve(true);
|
|
148
|
+
}
|
|
149
|
+
this.io.sockets.sockets.forEach((socket) => {
|
|
150
|
+
socket.disconnect();
|
|
151
|
+
})
|
|
152
|
+
this.io.close((err) => {
|
|
153
|
+
if(err){
|
|
154
|
+
reject(err);
|
|
155
|
+
}
|
|
156
|
+
resolve(true);
|
|
157
|
+
})
|
|
158
|
+
this.io = null;
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async connect(target: string): Promise<TypedRPCConnection> {
|
|
163
|
+
const SocketIOClientModule = await import("socket.io-client").catch(() => null);
|
|
164
|
+
if(!SocketIOClientModule){
|
|
165
|
+
throw new Error("socket.io-client module not found");
|
|
166
|
+
}
|
|
167
|
+
return new Promise<TypedRPCConnection>((resolve,reject) => {
|
|
168
|
+
const socket = SocketIOClientModule.io(`ws://${target}`);
|
|
169
|
+
const connection = new TypedRPCConnectionSocketIO({
|
|
170
|
+
id:socket.id || IdMaker.makeId(),
|
|
171
|
+
send:(data) => {
|
|
172
|
+
socket.emit('message',data);
|
|
173
|
+
},
|
|
174
|
+
close:() => {
|
|
175
|
+
socket.disconnect();
|
|
176
|
+
return true;
|
|
177
|
+
},
|
|
178
|
+
isClosed:() => {
|
|
179
|
+
return socket.disconnected;
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
this.emitter.emit('connection',connection);// 告知TypedRPCHandler有新连接
|
|
183
|
+
socket.on('connect',() => {
|
|
184
|
+
resolve(connection)
|
|
185
|
+
})
|
|
186
|
+
socket.on('message',(data) => {
|
|
187
|
+
connection.msgEmitter.emit('receive',data);
|
|
188
|
+
})
|
|
189
|
+
socket.on('close',() => {
|
|
190
|
+
connection.close();
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export {
|
|
198
|
+
TypedRPCConnectionSocketIO,
|
|
199
|
+
TypedRPCConnectionProviderSocketIO,
|
|
200
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TypedRPCConnectionHTTP, TypedRPCConnectionProviderHTTP } from "./connecitons/http.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const TypedRPCConnectionDefault = TypedRPCConnectionHTTP;
|
|
5
|
+
const TypedRPCConnectionProviderDefault = TypedRPCConnectionProviderHTTP;
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
TypedRPCConnectionDefault,
|
|
9
|
+
TypedRPCConnectionProviderDefault,
|
|
10
|
+
}
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TypedRPCConnection } from "./connecitons/basic.js";
|
|
2
|
+
import type { TypedRPCPacket } from "./packet.js"
|
|
3
|
+
|
|
4
|
+
type TypedRPCContext = {
|
|
5
|
+
connection:TypedRPCConnection,
|
|
6
|
+
inbound?:TypedRPCPacket,
|
|
7
|
+
outbound?:TypedRPCPacket,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const TypedRPCContextSymbol = Symbol("TypedRPCContext");
|
|
11
|
+
interface TypedRPCContextAware{
|
|
12
|
+
[TypedRPCContextSymbol]:TypedRPCContext | null,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
TypedRPCContextAware,
|
|
17
|
+
TypedRPCContext,
|
|
18
|
+
}
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { TypedRPCConnection, TypedRPCConnectionProvider } from "./connecitons/basic.js"
|
|
2
|
+
import { TypedRPCConnectionProviderDefault } from "./connection.js"
|
|
3
|
+
import { TypedRPCHandler } from "./handler.js"
|
|
4
|
+
import { TypedRPCPacketFactory, type TypedRPCRequestPacket, type TypedRPCResponsePacket } from "./packet.js"
|
|
5
|
+
import { TypedEmitter } from "./utils.js"
|
|
6
|
+
|
|
7
|
+
type TypedRPCCoreConfig = {
|
|
8
|
+
connection?:{
|
|
9
|
+
provider:TypedRPCConnectionProvider,
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type TypedRPCCoreEvents = {
|
|
14
|
+
connection:(connection:TypedRPCConnection)=>void,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class TypedRPCCore{
|
|
18
|
+
|
|
19
|
+
public emitter = new TypedEmitter<TypedRPCCoreEvents>();
|
|
20
|
+
private config:TypedRPCCoreConfig;
|
|
21
|
+
public handler:TypedRPCHandler;
|
|
22
|
+
|
|
23
|
+
constructor(config:TypedRPCCoreConfig){
|
|
24
|
+
const defaultConfig:TypedRPCCoreConfig = {
|
|
25
|
+
connection:{
|
|
26
|
+
provider:new TypedRPCConnectionProviderDefault(),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
this.config = {...defaultConfig,...config};
|
|
30
|
+
this.handler = new TypedRPCHandler();
|
|
31
|
+
this.init();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private init(){
|
|
35
|
+
const provider = this.config.connection?.provider;
|
|
36
|
+
if(provider){
|
|
37
|
+
provider.emitter.on('connection',(connection) => {
|
|
38
|
+
this.emitter.emit('connection',connection);
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
this.emitter.on('connection',(connection) => {
|
|
42
|
+
// 提供对向请求处理
|
|
43
|
+
connection.emitter.on('request',(context) => {
|
|
44
|
+
try{
|
|
45
|
+
const requestPacket = JSON.parse(context.data);
|
|
46
|
+
if(!TypedRPCPacketFactory.isRequestPacket(requestPacket)){
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this.handler.handle(connection,requestPacket,(responsePacket) => {
|
|
50
|
+
if(!responsePacket){
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
context.response(JSON.stringify(responsePacket));
|
|
54
|
+
});
|
|
55
|
+
}catch(e){
|
|
56
|
+
console.error(e);
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get hook(){
|
|
63
|
+
return this.handler.hook.bind(this.handler);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public async request(config:{
|
|
67
|
+
connection:TypedRPCConnection,
|
|
68
|
+
serviceName:string,
|
|
69
|
+
methodName:string,
|
|
70
|
+
args:any[],
|
|
71
|
+
}):Promise<{
|
|
72
|
+
request:TypedRPCRequestPacket,
|
|
73
|
+
response:TypedRPCResponsePacket,
|
|
74
|
+
}>{
|
|
75
|
+
const requestPacket = TypedRPCPacketFactory.createRequestPacket({
|
|
76
|
+
serviceName:config.serviceName,
|
|
77
|
+
methodName:config.methodName,
|
|
78
|
+
args:config.args,
|
|
79
|
+
})
|
|
80
|
+
const responsePacket = await this.handler.request(config.connection,requestPacket).catch((e) => {
|
|
81
|
+
return TypedRPCPacketFactory.createResponsePacket({
|
|
82
|
+
requestId:requestPacket.id,
|
|
83
|
+
error:e
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
if(!TypedRPCPacketFactory.isResponsePacket(responsePacket)){
|
|
87
|
+
throw new Error(`Invalid response packet:\n${JSON.stringify(responsePacket)}`);
|
|
88
|
+
}
|
|
89
|
+
if(responsePacket.requestId != requestPacket.id){
|
|
90
|
+
throw new Error(`Invalid response packet:request id not match\n${JSON.stringify(requestPacket)}\n${JSON.stringify(responsePacket)}`);
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
request:requestPacket,
|
|
94
|
+
response:responsePacket,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public async listen(config:{
|
|
99
|
+
port:number,
|
|
100
|
+
hostname?:string,
|
|
101
|
+
}):Promise<boolean>{
|
|
102
|
+
const provider = this.config.connection?.provider;
|
|
103
|
+
if(!provider){
|
|
104
|
+
throw new Error("Connection provider not found");
|
|
105
|
+
}
|
|
106
|
+
return provider.listen(config);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public async close():Promise<boolean>{
|
|
110
|
+
const provider = this.config.connection?.provider;
|
|
111
|
+
if(!provider){
|
|
112
|
+
throw new Error("Connection provider not found");
|
|
113
|
+
}
|
|
114
|
+
return provider.close();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public async connect(target: string):Promise<TypedRPCConnection>{
|
|
118
|
+
const provider = this.config.connection?.provider;
|
|
119
|
+
if(!provider){
|
|
120
|
+
throw new Error("Connection provider not found");
|
|
121
|
+
}
|
|
122
|
+
const connection = await provider.connect(target);
|
|
123
|
+
return connection;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
TypedRPCCore
|
|
130
|
+
}
|
package/src/define.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
|
|
2
|
+
type TypedRPCAPIDefineType = Record<string, Record<string, any>>
|
|
3
|
+
|
|
4
|
+
type TypedRPCDefineConfig = {
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class TypedRPCAPIDefine<T extends TypedRPCAPIDefineType> {
|
|
11
|
+
|
|
12
|
+
static TypedRPCService = Symbol('TypedRPCService');
|
|
13
|
+
static TypedRPCMethod = Symbol('TypedRPCMethod');
|
|
14
|
+
static TypedRPCMethodList = Symbol('TypedRPCMethodList');
|
|
15
|
+
|
|
16
|
+
constructor(config?:TypedRPCAPIDefineType){
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static method(){
|
|
21
|
+
return function(target:any, propertyKey:string, descriptor:PropertyDescriptor){
|
|
22
|
+
descriptor.value[TypedRPCAPIDefine.TypedRPCMethod] = true;
|
|
23
|
+
if(!target[TypedRPCAPIDefine.TypedRPCService]){
|
|
24
|
+
target[TypedRPCAPIDefine.TypedRPCService] = true;
|
|
25
|
+
}
|
|
26
|
+
if(!target[TypedRPCAPIDefine.TypedRPCMethodList]){
|
|
27
|
+
target[TypedRPCAPIDefine.TypedRPCMethodList] = new Set<string>();
|
|
28
|
+
}
|
|
29
|
+
target[TypedRPCAPIDefine.TypedRPCMethodList].add(propertyKey);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static isService(target:any){
|
|
34
|
+
return target[TypedRPCAPIDefine.TypedRPCService] === true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static isMethod(method:any){
|
|
38
|
+
// 判断是否是方法
|
|
39
|
+
if(typeof method !== 'function'){
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if(method[TypedRPCAPIDefine.TypedRPCMethod] != true){
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static getMethodList(service:any):string[]{
|
|
49
|
+
if(!TypedRPCAPIDefine.isService(service)){
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return Array.from(service[TypedRPCAPIDefine.TypedRPCMethodList]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export type {
|
|
58
|
+
TypedRPCAPIDefineType,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
TypedRPCAPIDefine,
|
|
63
|
+
}
|
package/src/handler.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
import type { TypedRPCConnection } from "./connecitons/basic.js";
|
|
3
|
+
import { TypedRPCContextSymbol, type TypedRPCContext } from "./context.js";
|
|
4
|
+
import { TypedRPCPacketFactory, type TypedRPCPacket, type TypedRPCRequestPacket, type TypedRPCResponsePacket } from "./packet.js";
|
|
5
|
+
import { TypedEmitter } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// interface TypedRPCHandlerMiddleware{
|
|
9
|
+
// inbound?:(context:TypedRPCContext) => Promise<TypedRPCContext>;
|
|
10
|
+
// outbound?:(context:TypedRPCContext) => Promise<TypedRPCContext>;
|
|
11
|
+
// }
|
|
12
|
+
|
|
13
|
+
class TypedRPCHandlerMiddleware{
|
|
14
|
+
async inbound(context:TypedRPCContext):Promise<TypedRPCContext>{
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
async outbound(context:TypedRPCContext):Promise<TypedRPCContext>{
|
|
18
|
+
return context;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type TypedRPCHandlerEvents = {
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 处理所有TypedRPC数据包,管理入站出站
|
|
27
|
+
*/
|
|
28
|
+
class TypedRPCHandler{
|
|
29
|
+
|
|
30
|
+
public emitter = new TypedEmitter<TypedRPCHandlerEvents>();
|
|
31
|
+
|
|
32
|
+
private middlewares:TypedRPCHandlerMiddleware[] = [];
|
|
33
|
+
|
|
34
|
+
private hooks:Record<string,Record<string,{
|
|
35
|
+
handler:(...args:any[])=>any,
|
|
36
|
+
bind:any,
|
|
37
|
+
}>> = {};
|
|
38
|
+
|
|
39
|
+
constructor(){
|
|
40
|
+
this.use({
|
|
41
|
+
inbound:async (context) => {
|
|
42
|
+
// Fallback 托底返回错误
|
|
43
|
+
if(context.inbound && !context.outbound){
|
|
44
|
+
context.outbound = TypedRPCPacketFactory.createResponsePacket({
|
|
45
|
+
requestId:context.inbound.id,
|
|
46
|
+
error:"service not available or not found",
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
return context;
|
|
50
|
+
},
|
|
51
|
+
outbound:async (context) => {
|
|
52
|
+
return context;
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
this.use({
|
|
56
|
+
inbound:async (context) => {
|
|
57
|
+
if(!context.inbound
|
|
58
|
+
|| !TypedRPCPacketFactory.isRequestPacket(context.inbound)){
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
61
|
+
// 如果已经有出站包,直接返回
|
|
62
|
+
if(context.outbound){
|
|
63
|
+
return context;
|
|
64
|
+
}
|
|
65
|
+
const serviceName = context.inbound.serviceName;
|
|
66
|
+
const methodName = context.inbound.methodName;
|
|
67
|
+
const args = context.inbound.args;
|
|
68
|
+
const hook = this.hooks[serviceName]?.[methodName];
|
|
69
|
+
if(!hook){
|
|
70
|
+
return context;
|
|
71
|
+
}
|
|
72
|
+
const result = await hook.handler.call(new Proxy(hook.bind || {},{
|
|
73
|
+
get(target,prop){
|
|
74
|
+
if(prop === TypedRPCContextSymbol){
|
|
75
|
+
return context;
|
|
76
|
+
}
|
|
77
|
+
return Reflect.get(target,prop);
|
|
78
|
+
}
|
|
79
|
+
}),...args);
|
|
80
|
+
const response = TypedRPCPacketFactory.createResponsePacket({
|
|
81
|
+
requestId:context.inbound.id,
|
|
82
|
+
result:result,
|
|
83
|
+
});
|
|
84
|
+
context.outbound = response;
|
|
85
|
+
return context;
|
|
86
|
+
},
|
|
87
|
+
outbound:async (context) => {
|
|
88
|
+
return context;
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public use(middleware:TypedRPCHandlerMiddleware){
|
|
94
|
+
this.middlewares.push(middleware);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async middlewareProcesser(context:TypedRPCContext,index:number,direction:'inbound'|'outbound'):Promise<TypedRPCContext>{
|
|
99
|
+
const middleware = this.middlewares[index];
|
|
100
|
+
if(!middleware){
|
|
101
|
+
return context;
|
|
102
|
+
}
|
|
103
|
+
// 执行中间件
|
|
104
|
+
if(direction == 'inbound'){
|
|
105
|
+
if(middleware.inbound){
|
|
106
|
+
context = await middleware.inbound(context);
|
|
107
|
+
}
|
|
108
|
+
index--;
|
|
109
|
+
}
|
|
110
|
+
if(direction == 'outbound'){
|
|
111
|
+
if(middleware.outbound){
|
|
112
|
+
context = await middleware.outbound(context);
|
|
113
|
+
}
|
|
114
|
+
index++;
|
|
115
|
+
}
|
|
116
|
+
return this.middlewareProcesser(context,index,direction);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public async outbound(context:TypedRPCContext):Promise<TypedRPCContext>{
|
|
120
|
+
if(this.middlewares.length <= 0){
|
|
121
|
+
return context;
|
|
122
|
+
}
|
|
123
|
+
return this.middlewareProcesser(context,0,'outbound');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public async inbound(context:TypedRPCContext):Promise<TypedRPCContext>{
|
|
127
|
+
if(this.middlewares.length <= 0){
|
|
128
|
+
return context;
|
|
129
|
+
}
|
|
130
|
+
return this.middlewareProcesser(context,this.middlewares.length - 1,'inbound');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public async request(connection:TypedRPCConnection,request:TypedRPCRequestPacket):Promise<TypedRPCPacket>{
|
|
134
|
+
let context:TypedRPCContext = {
|
|
135
|
+
connection:connection,
|
|
136
|
+
outbound:request,
|
|
137
|
+
}
|
|
138
|
+
context = await this.outbound(context);
|
|
139
|
+
if(!context.outbound){
|
|
140
|
+
throw new Error("Request failed: outbound is empty");
|
|
141
|
+
}
|
|
142
|
+
const res = await connection.request(JSON.stringify(context.outbound));
|
|
143
|
+
if(!res){
|
|
144
|
+
throw new Error("Response is empty");
|
|
145
|
+
}
|
|
146
|
+
let responseObject:any | null = null;
|
|
147
|
+
try{
|
|
148
|
+
responseObject = JSON.parse(res);
|
|
149
|
+
}catch(e){
|
|
150
|
+
throw new Error("Response is not a valid JSON string:"+res);
|
|
151
|
+
}
|
|
152
|
+
if(!TypedRPCPacketFactory.isPacket(responseObject)){
|
|
153
|
+
throw new Error("Response is not a TypedRPCPacket");
|
|
154
|
+
}
|
|
155
|
+
if(!TypedRPCPacketFactory.isResponsePacket(responseObject)){
|
|
156
|
+
throw new Error("Response is not a TypedRPCResponsePacket");
|
|
157
|
+
}
|
|
158
|
+
context.inbound = responseObject;
|
|
159
|
+
context = await this.inbound(context);
|
|
160
|
+
if(!context.inbound){
|
|
161
|
+
throw new Error("Request failed: inbound is empty");
|
|
162
|
+
}
|
|
163
|
+
return context.inbound;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public async handle(connection:TypedRPCConnection,request:TypedRPCRequestPacket,response:(packet:TypedRPCResponsePacket) => void){
|
|
167
|
+
let context:TypedRPCContext = {
|
|
168
|
+
connection:connection,
|
|
169
|
+
inbound:request,
|
|
170
|
+
}
|
|
171
|
+
context = await this.inbound(context);
|
|
172
|
+
if(!context.outbound){
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
context = await this.outbound(context);
|
|
176
|
+
if(!context.outbound
|
|
177
|
+
|| !TypedRPCPacketFactory.isResponsePacket(context.outbound)){
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
response(context.outbound);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
hook(serviceName:string,methodName:string,config:{
|
|
184
|
+
handler:(...args:any[])=>any,
|
|
185
|
+
bind?:any,
|
|
186
|
+
}){
|
|
187
|
+
if(!this.hooks[serviceName]){
|
|
188
|
+
this.hooks[serviceName] = {};
|
|
189
|
+
}
|
|
190
|
+
this.hooks[serviceName]![methodName] = {
|
|
191
|
+
handler:config.handler,
|
|
192
|
+
bind:config.bind,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
TypedRPCHandler,
|
|
201
|
+
TypedRPCHandlerMiddleware
|
|
202
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { TypedRPCClient } from './client.js';
|
|
2
|
+
import { TypedRPCServer } from './server.js';
|
|
3
|
+
import { TypedRPCAPIDefine } from './define.js';
|
|
4
|
+
import { TypedRPCConnectionDefault, TypedRPCConnectionProviderDefault } from './connection.js';
|
|
5
|
+
import { TypedRPCHandlerMiddleware } from './handler.js';
|
|
6
|
+
import { TypedRPCPacketFactory } from './packet.js';
|
|
7
|
+
import { TypedRPCContextSymbol, type TypedRPCContext, type TypedRPCContextAware } from './context.js';
|
|
8
|
+
import { TypedRPCConnection, TypedRPCConnectionProvider } from './connecitons/basic.js';
|
|
9
|
+
import { TypedRPCConnectionHTTP, TypedRPCConnectionProviderHTTP } from './connecitons/http.js';
|
|
10
|
+
import { TypedRPCConnectionProviderSocket, TypedRPCConnectionSocket } from './connecitons/socket.js';
|
|
11
|
+
import { TypedRPCConnectionSocketIO } from './connecitons/socketio.js';
|
|
12
|
+
import { TypedRPCConnectionProviderSocketIO } from './connecitons/socketio.js';
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
TypedRPCContext,
|
|
16
|
+
TypedRPCContextAware,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
TypedRPCClient,
|
|
21
|
+
TypedRPCServer,
|
|
22
|
+
TypedRPCAPIDefine,
|
|
23
|
+
|
|
24
|
+
TypedRPCConnection,
|
|
25
|
+
TypedRPCConnectionProvider,
|
|
26
|
+
|
|
27
|
+
TypedRPCConnectionDefault,
|
|
28
|
+
TypedRPCConnectionProviderDefault,
|
|
29
|
+
TypedRPCConnectionHTTP,
|
|
30
|
+
TypedRPCConnectionProviderHTTP,
|
|
31
|
+
TypedRPCConnectionSocket,
|
|
32
|
+
TypedRPCConnectionProviderSocket,
|
|
33
|
+
TypedRPCConnectionSocketIO,
|
|
34
|
+
TypedRPCConnectionProviderSocketIO,
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
TypedRPCHandlerMiddleware,
|
|
38
|
+
TypedRPCPacketFactory,
|
|
39
|
+
|
|
40
|
+
TypedRPCContextSymbol,
|
|
41
|
+
}
|