@wishcore/wish-rpc 0.6.14 → 0.6.15
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/dist/src/index.d.ts +43 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/method-registry.d.ts +60 -0
- package/dist/src/method-registry.js +135 -0
- package/dist/src/method-registry.js.map +1 -0
- package/dist/src/rpc-server.d.ts +146 -42
- package/dist/src/rpc-server.js +337 -277
- package/dist/src/rpc-server.js.map +1 -1
- package/package.json +1 -1
- package/dist/test/stream-clean.spec.d.ts +0 -1
- package/dist/test/stream-clean.spec.js +0 -146
- package/dist/test/stream-clean.spec.js.map +0 -1
- package/dist/test/stream.spec.d.ts +0 -1
- package/dist/test/stream.spec.js +0 -113
- package/dist/test/stream.spec.js.map +0 -1
- package/dist/test/test-client.d.ts +0 -1
- package/dist/test/test-client.js +0 -88
- package/dist/test/test-client.js.map +0 -1
- package/dist/test/test-rpc-acl.d.ts +0 -1
- package/dist/test/test-rpc-acl.js +0 -103
- package/dist/test/test-rpc-acl.js.map +0 -1
- package/dist/test/test-rpc.spec.d.ts +0 -1
- package/dist/test/test-rpc.spec.js +0 -143
- package/dist/test/test-rpc.spec.js.map +0 -1
- package/dist/test/test-session.spec.d.ts +0 -1
- package/dist/test/test-session.spec.js +0 -46
- package/dist/test/test-session.spec.js.map +0 -1
- package/dist/test/test-stream.d.ts +0 -1
- package/dist/test/test-stream.js +0 -253
- package/dist/test/test-stream.js.map +0 -1
package/dist/src/rpc-server.js
CHANGED
|
@@ -1,43 +1,96 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Server = void 0;
|
|
4
|
+
const method_registry_1 = require("./method-registry");
|
|
5
|
+
const debug = console.log;
|
|
2
6
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* RPC Server for registering and handling remote procedure calls.
|
|
8
|
+
*
|
|
9
|
+
* Supports both modern endpoint registration and legacy underscore-prefixed methods.
|
|
10
|
+
* Provides real-time communication with support for streaming, events, and async operations.
|
|
11
|
+
*
|
|
12
|
+
* Type-safe endpoint registration with custom configuration:
|
|
13
|
+
* ```typescript
|
|
14
|
+
* interface ReasonEndpoint extends BaseEndpointConfig {
|
|
15
|
+
* validateArgs?: (args: any[], context: any) => boolean;
|
|
16
|
+
* permissions?: string[];
|
|
17
|
+
* rateLimit?: number;
|
|
18
|
+
* }
|
|
7
19
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
20
|
+
* const server = new Server<ReasonEndpoint>();
|
|
21
|
+
* server.endpoint('user.get', {
|
|
22
|
+
* doc: 'Get user by ID',
|
|
23
|
+
* args: 'userId: string',
|
|
24
|
+
* validateArgs: (args, context) => validateUserArgs(args),
|
|
25
|
+
* permissions: ['user', 'admin'],
|
|
26
|
+
* rateLimit: 100
|
|
27
|
+
* }, async (req, res) => {
|
|
28
|
+
* const user = await getUserById(req.args[0]);
|
|
29
|
+
* res.send(user);
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
11
32
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
33
|
+
* Default usage (backwards compatible):
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const server = new Server();
|
|
36
|
+
* server.endpoint('user.get', {
|
|
37
|
+
* doc: 'Get user by ID',
|
|
38
|
+
* args: 'userId: string',
|
|
39
|
+
* validate: (args, context) => validateUserArgs(args)
|
|
40
|
+
* }, async (req, res) => {
|
|
41
|
+
* const user = await getUserById(req.args[0]);
|
|
42
|
+
* res.send(user);
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
14
45
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
46
|
+
* Legacy method registration (backwards compatibility):
|
|
47
|
+
* ```typescript
|
|
48
|
+
* server.insertMethods({
|
|
49
|
+
* _getUser: {
|
|
50
|
+
* doc: 'Get user by ID',
|
|
51
|
+
* args: 'userId: string'
|
|
52
|
+
* },
|
|
53
|
+
* getUser: async (req, res) => {
|
|
54
|
+
* const user = await getUserById(req.args[0]);
|
|
55
|
+
* res.send(user);
|
|
56
|
+
* }
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* Accessing metadata for custom validation and introspection:
|
|
61
|
+
* ```typescript
|
|
62
|
+
* // Get method metadata and check for custom validation properties
|
|
63
|
+
* const meta = server.getMethodMeta('user.get');
|
|
64
|
+
* if (meta?.validateArgs) {
|
|
65
|
+
* const isValid = meta.validateArgs(args, context);
|
|
66
|
+
* }
|
|
67
|
+
* if (meta?.schema) {
|
|
68
|
+
* const result = meta.schema.safeParse(args); // e.g., Zod
|
|
69
|
+
* }
|
|
70
|
+
* if (meta?.validator) {
|
|
71
|
+
* meta.validator.validate(args); // e.g., Joi
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* // Generate documentation from all methods
|
|
75
|
+
* const allMeta = server.getAllMethodMeta();
|
|
76
|
+
* for (const [name, config] of Object.entries(allMeta)) {
|
|
77
|
+
* console.log(`${name}: ${config.doc || 'No documentation'}`);
|
|
78
|
+
* if (config.args) console.log(` Args: ${config.args}`);
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* Features:
|
|
83
|
+
* - Type-safe endpoint registration with TypeScript
|
|
84
|
+
* - Streaming support for large data transfers
|
|
85
|
+
* - Real-time events and signals
|
|
86
|
+
* - Request validation and error handling
|
|
87
|
+
* - Session management and cleanup
|
|
88
|
+
* - Method discovery and documentation
|
|
89
|
+
* - Metadata access for external validation and introspection
|
|
33
90
|
*/
|
|
34
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.Server = void 0;
|
|
36
|
-
const debug = console.log;
|
|
37
91
|
class Server {
|
|
38
92
|
constructor(methods) {
|
|
39
|
-
this.
|
|
40
|
-
this.methods = {};
|
|
93
|
+
this.registry = new method_registry_1.MethodRegistry();
|
|
41
94
|
this.requests = {};
|
|
42
95
|
this.clientIdCounter = 0;
|
|
43
96
|
/** internal request id counter */
|
|
@@ -58,85 +111,87 @@ class Server {
|
|
|
58
111
|
delete this.requests[i];
|
|
59
112
|
}
|
|
60
113
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const prefix = path ? path + '.' : '';
|
|
68
|
-
for (const i in o) {
|
|
69
|
-
if (i === '_') {
|
|
70
|
-
// module meta
|
|
71
|
-
}
|
|
72
|
-
else if (i.substring(0, 1) !== '_') {
|
|
73
|
-
if (o['_' + i]) {
|
|
74
|
-
// publish is _funcName exists for funcName
|
|
75
|
-
if (typeof o[i] === 'function') {
|
|
76
|
-
const ip = o['_' + i].name || i;
|
|
77
|
-
o['_' + i].fullname = prefix + ip;
|
|
78
|
-
try {
|
|
79
|
-
this.modules[prefix + ip] = o['_' + i];
|
|
80
|
-
this.methods[prefix + ip] = o[i];
|
|
81
|
-
}
|
|
82
|
-
catch (e) {
|
|
83
|
-
console.log('Kaboom! ', e);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
else if (typeof o[i] === 'object') {
|
|
87
|
-
// this is a sub item
|
|
88
|
-
//this.modules[prefix + i] = o['_' + i];
|
|
89
|
-
this.addMethods(prefix + i, o[i]);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// no _funcName found, do not publish via RPC
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
114
|
+
/**
|
|
115
|
+
* Legacy method for backwards compatibility
|
|
116
|
+
* Registers methods using the old underscore-prefixed structure
|
|
117
|
+
*/
|
|
118
|
+
insertMethods(methodMap) {
|
|
119
|
+
this.registry.registerLegacyMethods(methodMap);
|
|
97
120
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
d[j] = s[j];
|
|
107
|
-
}
|
|
108
|
-
return d;
|
|
121
|
+
/**
|
|
122
|
+
* Lists all available methods and their configurations
|
|
123
|
+
*/
|
|
124
|
+
listMethods(args, context, callback) {
|
|
125
|
+
try {
|
|
126
|
+
const result = this.registry.getMethodListing();
|
|
127
|
+
callback(null, result);
|
|
109
128
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
for (const i in this.modules) {
|
|
113
|
-
result[i] = copy(this.modules[i], filter);
|
|
129
|
+
catch (error) {
|
|
130
|
+
callback(error);
|
|
114
131
|
}
|
|
115
|
-
cb(null, result);
|
|
116
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Cleanup when a client goes offline
|
|
135
|
+
*/
|
|
117
136
|
clientOffline(clientId) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
137
|
+
if (!this.requests[clientId]) {
|
|
138
|
+
return; // Client not found
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
for (const requestId in this.requests[clientId]) {
|
|
142
|
+
this.cleanupRequest(clientId, requestId);
|
|
121
143
|
}
|
|
122
|
-
|
|
123
|
-
|
|
144
|
+
delete this.requests[clientId];
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(`Error during client ${clientId} offline cleanup:`, error);
|
|
124
148
|
}
|
|
125
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Close all active connections and cleanup
|
|
152
|
+
*/
|
|
126
153
|
closeAll() {
|
|
127
|
-
|
|
128
|
-
for (const
|
|
129
|
-
|
|
130
|
-
this.
|
|
154
|
+
try {
|
|
155
|
+
for (const clientId in this.requests) {
|
|
156
|
+
for (const requestId in this.requests[clientId]) {
|
|
157
|
+
this.cleanupRequest(parseInt(clientId), requestId);
|
|
131
158
|
}
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
delete this.requests[clientId];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
console.error('Error during closeAll cleanup:', error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Safely cleanup a specific request
|
|
168
|
+
*/
|
|
169
|
+
cleanupRequest(clientId, requestId) {
|
|
170
|
+
var _a, _b;
|
|
171
|
+
try {
|
|
172
|
+
const request = (_a = this.requests[clientId]) === null || _a === void 0 ? void 0 : _a[requestId];
|
|
173
|
+
if (request && typeof request.end === 'function') {
|
|
174
|
+
request.end();
|
|
134
175
|
}
|
|
176
|
+
(_b = this.requests[clientId]) === null || _b === void 0 ? true : delete _b[requestId];
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
console.error(`Error cleaning up request ${requestId} for client ${clientId}:`, error);
|
|
135
180
|
}
|
|
136
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Parse and handle incoming RPC messages
|
|
184
|
+
*/
|
|
137
185
|
parse(msg, respond, context, clientId) {
|
|
138
186
|
if (typeof clientId !== 'number') {
|
|
139
|
-
|
|
187
|
+
console.warn('Got RPC request from unspecified client');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!msg || typeof msg.op !== 'string') {
|
|
191
|
+
respond({
|
|
192
|
+
err: msg === null || msg === void 0 ? void 0 : msg.id,
|
|
193
|
+
data: { code: 400, msg: 'Invalid message format' }
|
|
194
|
+
});
|
|
140
195
|
return;
|
|
141
196
|
}
|
|
142
197
|
try {
|
|
@@ -154,7 +209,6 @@ class Server {
|
|
|
154
209
|
}
|
|
155
210
|
if (msg.end) {
|
|
156
211
|
const id = msg.end;
|
|
157
|
-
//console.log("end this request:", clientId, id, this.requests[clientId] ? this.requests[clientId][id] : this.requests);
|
|
158
212
|
if (this.requests[clientId] && this.requests[clientId][id]) {
|
|
159
213
|
if (typeof this.requests[clientId][id].end === 'function') {
|
|
160
214
|
this.requests[clientId][id].end();
|
|
@@ -164,10 +218,10 @@ class Server {
|
|
|
164
218
|
return;
|
|
165
219
|
}
|
|
166
220
|
if (!this.requests[clientId][id]) {
|
|
167
|
-
return;
|
|
221
|
+
return;
|
|
168
222
|
}
|
|
169
223
|
if (typeof this.requests[clientId][id].end === 'function') {
|
|
170
|
-
this.requests[id].end();
|
|
224
|
+
this.requests[clientId][id].end();
|
|
171
225
|
}
|
|
172
226
|
return;
|
|
173
227
|
}
|
|
@@ -177,8 +231,7 @@ class Server {
|
|
|
177
231
|
});
|
|
178
232
|
return;
|
|
179
233
|
}
|
|
180
|
-
if (
|
|
181
|
-
// service not found
|
|
234
|
+
if (!this.registry.hasMethod(msg.op)) {
|
|
182
235
|
respond({ err: msg.id, data: { code: 300, msg: 'No method found: ' + msg.op } });
|
|
183
236
|
return;
|
|
184
237
|
}
|
|
@@ -207,140 +260,156 @@ class Server {
|
|
|
207
260
|
}
|
|
208
261
|
}
|
|
209
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Process raw RPC invocation
|
|
265
|
+
*/
|
|
210
266
|
invokeRaw(msg, respond, context, clientId) {
|
|
211
|
-
var _a, _b;
|
|
212
267
|
++this.requestId;
|
|
213
268
|
if (!this.requests[clientId]) {
|
|
214
269
|
return console.log('Session not opened for client. Use Server.open() first');
|
|
215
270
|
}
|
|
216
271
|
if (msg.push) {
|
|
217
|
-
|
|
218
|
-
const ctx = this.requests[clientId][msg.push];
|
|
219
|
-
//console.log('stream context:', ctx);
|
|
220
|
-
if (msg.data === null) {
|
|
221
|
-
console.log('msg.data is null:', msg);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
ctx.data(msg.data);
|
|
272
|
+
this.handlePushMessage(msg, clientId);
|
|
225
273
|
return;
|
|
226
274
|
}
|
|
227
275
|
if (msg.sig) {
|
|
228
|
-
|
|
229
|
-
const ctx = this.requests[clientId][msg.sig];
|
|
230
|
-
// call the actual method
|
|
231
|
-
try {
|
|
232
|
-
this.methods[ctx.op].call(this.requests[clientId][msg.sig], msg.data, {
|
|
233
|
-
send: (data) => {
|
|
234
|
-
if (typeof ctx.end === 'function') {
|
|
235
|
-
ctx.end();
|
|
236
|
-
}
|
|
237
|
-
if (this.requests[clientId][msg.sig]) {
|
|
238
|
-
delete this.requests[clientId][msg.sig];
|
|
239
|
-
}
|
|
240
|
-
respond({ ack: msg.sig, data: data });
|
|
241
|
-
},
|
|
242
|
-
emit: (data) => {
|
|
243
|
-
if (!this.requests[clientId][msg.sig]) {
|
|
244
|
-
if (typeof ctx.end === 'function') {
|
|
245
|
-
ctx.end();
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return respond({ sig: msg.sig, data: data });
|
|
249
|
-
},
|
|
250
|
-
error: (data) => {
|
|
251
|
-
if (typeof ctx.end === 'function') {
|
|
252
|
-
ctx.end();
|
|
253
|
-
}
|
|
254
|
-
delete this.requests[clientId][msg.sig];
|
|
255
|
-
respond({ err: msg.sig, data: data });
|
|
256
|
-
},
|
|
257
|
-
close: (data) => {
|
|
258
|
-
if (typeof ctx.end === 'function') {
|
|
259
|
-
ctx.end();
|
|
260
|
-
}
|
|
261
|
-
delete this.requests[clientId][msg.sig];
|
|
262
|
-
respond({ fin: msg.sig });
|
|
263
|
-
}
|
|
264
|
-
}, context);
|
|
265
|
-
}
|
|
266
|
-
catch (e) {
|
|
267
|
-
console.log('Calling the method in RPC failed:', msg.op, msg.args, e.stack);
|
|
268
|
-
delete this.requests[clientId][msg.sig];
|
|
269
|
-
respond({ err: msg.sig, data: { msg: 'rpc failed during execution of ' + msg.op, code: 578 } });
|
|
270
|
-
}
|
|
276
|
+
this.handleSignalMessage(msg, respond, context, clientId);
|
|
271
277
|
return;
|
|
272
278
|
}
|
|
273
279
|
if (typeof msg.id === 'undefined') {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
280
|
+
this.handleEventMessage(msg, respond, context);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
this.handleRegularRequest(msg, respond, context, clientId);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Handle push (streaming data) messages
|
|
288
|
+
*/
|
|
289
|
+
handlePushMessage(msg, clientId) {
|
|
290
|
+
if (!msg.push) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const ctx = this.requests[clientId][msg.push];
|
|
294
|
+
if (!ctx) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (msg.data === null) {
|
|
298
|
+
console.log('msg.data is null:', msg);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
ctx.data(msg.data);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Handle signal (streaming) messages
|
|
305
|
+
*/
|
|
306
|
+
handleSignalMessage(msg, respond, context, clientId) {
|
|
307
|
+
if (!msg.sig) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const ctx = this.requests[clientId][msg.sig];
|
|
311
|
+
if (!ctx) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
const method = this.registry.getMethod(ctx.op);
|
|
316
|
+
if (method) {
|
|
317
|
+
method.call(this.requests[clientId][msg.sig], msg.data, this.createSignalResponseHandlers(msg.sig, respond, ctx, clientId), context);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch (e) {
|
|
321
|
+
console.log('Calling the method in RPC failed:', msg.op, msg.args, e.stack);
|
|
322
|
+
delete this.requests[clientId][msg.sig];
|
|
323
|
+
respond({ err: msg.sig, data: { msg: 'rpc failed during execution of ' + msg.op, code: 578 } });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Create response handlers for signal messages
|
|
328
|
+
*/
|
|
329
|
+
createSignalResponseHandlers(sigId, respond, ctx, clientId) {
|
|
330
|
+
return {
|
|
331
|
+
send: (data) => {
|
|
332
|
+
if (typeof ctx.end === 'function') {
|
|
333
|
+
ctx.end();
|
|
334
|
+
}
|
|
335
|
+
if (this.requests[clientId][sigId]) {
|
|
336
|
+
delete this.requests[clientId][sigId];
|
|
337
|
+
}
|
|
338
|
+
respond({ ack: sigId, data: data });
|
|
339
|
+
},
|
|
340
|
+
emit: (data) => {
|
|
341
|
+
if (!this.requests[clientId][sigId]) {
|
|
342
|
+
if (typeof ctx.end === 'function') {
|
|
343
|
+
ctx.end();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return respond({ sig: sigId, data: data });
|
|
347
|
+
},
|
|
348
|
+
error: (data) => {
|
|
349
|
+
if (typeof ctx.end === 'function') {
|
|
350
|
+
ctx.end();
|
|
351
|
+
}
|
|
352
|
+
delete this.requests[clientId][sigId];
|
|
353
|
+
respond({ err: sigId, data: data });
|
|
354
|
+
},
|
|
355
|
+
close: (data) => {
|
|
356
|
+
if (typeof ctx.end === 'function') {
|
|
357
|
+
ctx.end();
|
|
358
|
+
}
|
|
359
|
+
delete this.requests[clientId][sigId];
|
|
360
|
+
respond({ fin: sigId });
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Handle event messages (no response expected)
|
|
366
|
+
*/
|
|
367
|
+
handleEventMessage(msg, respond, context) {
|
|
368
|
+
const reqCtx = {};
|
|
369
|
+
const method = this.registry.getMethod(msg.op);
|
|
370
|
+
if (method) {
|
|
371
|
+
method.call(reqCtx, { args: msg.args }, {
|
|
277
372
|
send: () => { debug('Trying to send response to event (' + msg.op + '). Dropping.'); },
|
|
278
373
|
emit: () => { debug('Trying to emit response to event (' + msg.op + '). Dropping.'); },
|
|
279
374
|
error: () => { debug('Trying to respond with error to event (' + msg.op + '). Dropping.'); },
|
|
280
375
|
close: () => { debug('Trying to close event (' + msg.op + '). Dropping.'); }
|
|
281
376
|
}, context);
|
|
282
377
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Handle regular RPC requests
|
|
381
|
+
*/
|
|
382
|
+
handleRegularRequest(msg, respond, context, clientId) {
|
|
383
|
+
var _a, _b;
|
|
384
|
+
const reqCtx = {
|
|
385
|
+
id: msg.id,
|
|
386
|
+
op: msg.op,
|
|
387
|
+
args: msg.args,
|
|
388
|
+
context,
|
|
389
|
+
end: null,
|
|
390
|
+
send: respond,
|
|
391
|
+
};
|
|
392
|
+
if (!this.requests[clientId]) {
|
|
393
|
+
this.requests[clientId] = {};
|
|
394
|
+
}
|
|
395
|
+
// Check for duplicate request ID
|
|
396
|
+
if (msg.id && ((_a = this.requests[clientId]) === null || _a === void 0 ? void 0 : _a[msg.id])) {
|
|
397
|
+
console.log("Serious warning. There is already a request by that id, we'll kill it off! This session is likely not clean. clientId:", clientId, 'msg', msg, 'stack trace:', new Error().stack);
|
|
398
|
+
if (msg.id && typeof this.requests[clientId][msg.id].end === 'function') {
|
|
399
|
+
this.requests[clientId][msg.id].end();
|
|
295
400
|
}
|
|
296
|
-
if (
|
|
297
|
-
console.log("Serious warning. There is already a request by that id, we'll kill it off! This session is likely not clean. clientId:", clientId, 'msg', msg, 'stack trace:', new Error().stack);
|
|
298
|
-
if (typeof this.requests[clientId][msg.id].end === 'function') {
|
|
299
|
-
this.requests[clientId][msg.id].end();
|
|
300
|
-
}
|
|
401
|
+
if (msg.id) {
|
|
301
402
|
delete this.requests[clientId][msg.id];
|
|
302
|
-
console.log('Removed old request by same id from same client...');
|
|
303
403
|
}
|
|
404
|
+
console.log('Removed old request by same id from same client...');
|
|
405
|
+
}
|
|
406
|
+
if (msg.id) {
|
|
304
407
|
this.requests[clientId][msg.id] = reqCtx;
|
|
305
|
-
// call the actual method
|
|
306
408
|
try {
|
|
307
|
-
this.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
reqCtx.end();
|
|
312
|
-
}
|
|
313
|
-
if ((_a = this.requests[clientId]) === null || _a === void 0 ? void 0 : _a[msg.id]) {
|
|
314
|
-
delete this.requests[clientId][msg.id];
|
|
315
|
-
}
|
|
316
|
-
respond({ ack: msg.id, data: data });
|
|
317
|
-
},
|
|
318
|
-
emit: (data) => {
|
|
319
|
-
var _a;
|
|
320
|
-
if (!((_a = this.requests[clientId]) === null || _a === void 0 ? void 0 : _a[msg.id])) {
|
|
321
|
-
if (typeof reqCtx.end === 'function') {
|
|
322
|
-
reqCtx.end();
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return respond({ sig: msg.id, data: data });
|
|
326
|
-
},
|
|
327
|
-
error: (data) => {
|
|
328
|
-
var _a;
|
|
329
|
-
if (typeof reqCtx.end === 'function') {
|
|
330
|
-
reqCtx.end();
|
|
331
|
-
}
|
|
332
|
-
(_a = this.requests[clientId]) === null || _a === void 0 ? true : delete _a[msg.id];
|
|
333
|
-
respond({ err: msg.id, data: data });
|
|
334
|
-
},
|
|
335
|
-
close: (data) => {
|
|
336
|
-
var _a;
|
|
337
|
-
if (typeof reqCtx.end === 'function') {
|
|
338
|
-
reqCtx.end();
|
|
339
|
-
}
|
|
340
|
-
(_a = this.requests[clientId]) === null || _a === void 0 ? true : delete _a[msg.id];
|
|
341
|
-
respond({ fin: msg.id });
|
|
342
|
-
}
|
|
343
|
-
}, context);
|
|
409
|
+
const method = this.registry.getMethod(msg.op);
|
|
410
|
+
if (method) {
|
|
411
|
+
method.call(reqCtx, { args: msg.args }, this.createRegularResponseHandlers(msg.id, respond, reqCtx, clientId), context);
|
|
412
|
+
}
|
|
344
413
|
}
|
|
345
414
|
catch (e) {
|
|
346
415
|
console.log('Calling the method in RPC failed:', msg.op, msg.args, e.stack);
|
|
@@ -349,39 +418,48 @@ class Server {
|
|
|
349
418
|
}
|
|
350
419
|
}
|
|
351
420
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
421
|
+
/**
|
|
422
|
+
* Create response handlers for regular requests
|
|
423
|
+
*/
|
|
424
|
+
createRegularResponseHandlers(msgId, respond, reqCtx, clientId) {
|
|
425
|
+
return {
|
|
426
|
+
send: (data) => {
|
|
427
|
+
var _a;
|
|
428
|
+
if (typeof reqCtx.end === 'function') {
|
|
429
|
+
reqCtx.end();
|
|
430
|
+
}
|
|
431
|
+
if ((_a = this.requests[clientId]) === null || _a === void 0 ? void 0 : _a[msgId]) {
|
|
432
|
+
delete this.requests[clientId][msgId];
|
|
433
|
+
}
|
|
434
|
+
respond({ ack: msgId, data: data });
|
|
435
|
+
},
|
|
436
|
+
emit: (data) => {
|
|
437
|
+
var _a;
|
|
438
|
+
if (!((_a = this.requests[clientId]) === null || _a === void 0 ? void 0 : _a[msgId])) {
|
|
439
|
+
if (typeof reqCtx.end === 'function') {
|
|
440
|
+
reqCtx.end();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return respond({ sig: msgId, data: data });
|
|
444
|
+
},
|
|
445
|
+
error: (data) => {
|
|
446
|
+
var _a;
|
|
447
|
+
if (typeof reqCtx.end === 'function') {
|
|
448
|
+
reqCtx.end();
|
|
449
|
+
}
|
|
450
|
+
(_a = this.requests[clientId]) === null || _a === void 0 ? true : delete _a[msgId];
|
|
451
|
+
respond({ err: msgId, data: data });
|
|
452
|
+
},
|
|
453
|
+
close: (data) => {
|
|
454
|
+
var _a;
|
|
455
|
+
if (typeof reqCtx.end === 'function') {
|
|
456
|
+
reqCtx.end();
|
|
457
|
+
}
|
|
458
|
+
(_a = this.requests[clientId]) === null || _a === void 0 ? true : delete _a[msgId];
|
|
459
|
+
respond({ fin: msgId });
|
|
460
|
+
}
|
|
380
461
|
};
|
|
381
|
-
|
|
382
|
-
this.parse(msg, response, context, '__invoke');
|
|
383
462
|
}
|
|
384
|
-
*/
|
|
385
463
|
close(clientId) {
|
|
386
464
|
for (const i in this.requests[clientId]) {
|
|
387
465
|
if (typeof this.requests[clientId][i].end === 'function') {
|
|
@@ -403,40 +481,22 @@ class Server {
|
|
|
403
481
|
* @param handler - The endpoint handler function
|
|
404
482
|
*/
|
|
405
483
|
endpoint(name, config, handler) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const validationKey = `_${methodName}`;
|
|
423
|
-
current[validationKey] = {};
|
|
424
|
-
if (config.doc) {
|
|
425
|
-
current[validationKey].doc = config.doc;
|
|
426
|
-
}
|
|
427
|
-
if (config.args) {
|
|
428
|
-
current[validationKey].args = config.args;
|
|
429
|
-
}
|
|
430
|
-
if (config.validate) {
|
|
431
|
-
current[validationKey].validate = config.validate;
|
|
432
|
-
}
|
|
433
|
-
if (config.validateResult) {
|
|
434
|
-
current[validationKey].validateResult = config.validateResult;
|
|
435
|
-
}
|
|
436
|
-
// Add the handler
|
|
437
|
-
current[methodName] = handler;
|
|
438
|
-
// Register with the RPC server
|
|
439
|
-
this.insertMethods(methods);
|
|
484
|
+
this.registry.registerEndpoint(name, config, handler);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Get metadata for a specific method (including all custom properties)
|
|
488
|
+
* @param name - Method name
|
|
489
|
+
* @returns The complete method configuration or undefined if not found
|
|
490
|
+
*/
|
|
491
|
+
getMethodMeta(name) {
|
|
492
|
+
return this.registry.getModule(name);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Get metadata for all registered methods
|
|
496
|
+
* @returns Object containing all method configurations
|
|
497
|
+
*/
|
|
498
|
+
getAllMethodMeta() {
|
|
499
|
+
return this.registry.getModules();
|
|
440
500
|
}
|
|
441
501
|
}
|
|
442
502
|
exports.Server = Server;
|