libshv-js 3.13.0 → 3.14.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/package.json +1 -1
- package/src/ws-client.ts +91 -24
package/package.json
CHANGED
package/src/ws-client.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import {accessLevelFromAccessString} from './access';
|
|
1
2
|
import {ChainPackReader, CHAINPACK_PROTOCOL_TYPE, ChainPackWriter, toChainPack} from './chainpack';
|
|
2
3
|
import {type CponReader, CPON_PROTOCOL_TYPE, toCpon} from './cpon';
|
|
3
4
|
import {ERROR_MESSAGE, ErrorCode, ERROR_CODE, RpcMessageZod, type RpcMessage, isSignal, isRequest, type RpcRequest, isResponse, ERROR_DATA, type ErrorMap, RPC_MESSAGE_METHOD, RPC_MESSAGE_SHV_PATH, RPC_MESSAGE_REQUEST_ID, RPC_MESSAGE_PARAMS, RPC_MESSAGE_ERROR, RPC_MESSAGE_RESULT, RPC_MESSAGE_CALLER_IDS, RpcResponseValue, RPC_MESSAGE_DELAY, RPC_MESSAGE_ABORT, RpcSignal, RpcResponse, RPC_MESSAGE_ACCESS_LEVEL} from './rpcmessage';
|
|
4
5
|
import {type RpcValue, type Null, type Int, type IMap, type ShvMap, makeMap, makeIMap, RpcValueWithMetaData, makeMetaMap, Double} from './rpcvalue';
|
|
6
|
+
import * as z from './zod';
|
|
5
7
|
|
|
6
8
|
const DEFAULT_TIMEOUT = 5000;
|
|
7
9
|
const DEFAULT_PING_INTERVAL = 30 * 1000;
|
|
@@ -60,12 +62,27 @@ export class RequestHandler {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
export const LsParamZod = z.undefined().or(z.string());
|
|
66
|
+
export const DirParamZod = z.undefined().or(z.boolean()).or(z.string());
|
|
67
|
+
type DirParam = z.infer<typeof DirParamZod>;
|
|
68
|
+
|
|
69
|
+
export type DirEntry = IMap<{
|
|
70
|
+
[DIR_NAME]: string;
|
|
71
|
+
[DIR_FLAGS]: DirFlags;
|
|
72
|
+
[DIR_PARAM]: string | Null;
|
|
73
|
+
[DIR_RESULT]: string | Null;
|
|
74
|
+
[DIR_ACCESS]: Int;
|
|
75
|
+
[DIR_SIGNALS]: ShvMap<Record<string, string | Null>>;
|
|
76
|
+
[DIR_EXTRA]: ShvMap;
|
|
77
|
+
}>;
|
|
78
|
+
|
|
79
|
+
export type DirResult = DirEntry[];
|
|
80
|
+
|
|
81
|
+
export type MethodHandler = {
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
|
+
paramParser: z.ZodType<any>,
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
handler: (shvPath: string, method: string, params: any, delay: (progress: number) => void) => Promise<RpcValue> | RpcValue | RequestHandler,
|
|
69
86
|
};
|
|
70
87
|
|
|
71
88
|
type WsClientOptionsLogin = WsClientOptionsCommon & {
|
|
@@ -76,7 +93,7 @@ type WsClientOptionsLogin = WsClientOptionsCommon & {
|
|
|
76
93
|
onConnected: () => void;
|
|
77
94
|
onConnectionFailure: (error: Error) => void;
|
|
78
95
|
onDisconnected: () => void;
|
|
79
|
-
onRequest: (
|
|
96
|
+
onRequest: (shvPath: string) => Array<{entry: DirEntry} & MethodHandler> | undefined;
|
|
80
97
|
};
|
|
81
98
|
|
|
82
99
|
type WsClientOptionsWorkflows = WsClientOptionsCommon & {
|
|
@@ -102,15 +119,6 @@ export const DIR_RESULT = 4;
|
|
|
102
119
|
export const DIR_ACCESS = 5;
|
|
103
120
|
export const DIR_SIGNALS = 6;
|
|
104
121
|
export const DIR_EXTRA = 63;
|
|
105
|
-
export type DirResult = Array<IMap<{
|
|
106
|
-
[DIR_NAME]: string;
|
|
107
|
-
[DIR_FLAGS]: DirFlags;
|
|
108
|
-
[DIR_PARAM]: string | Null;
|
|
109
|
-
[DIR_RESULT]: string | Null;
|
|
110
|
-
[DIR_ACCESS]: Int;
|
|
111
|
-
[DIR_SIGNALS]: ShvMap<Record<string, string | Null>>;
|
|
112
|
-
[DIR_EXTRA]: ShvMap;
|
|
113
|
-
}>>;
|
|
114
122
|
|
|
115
123
|
class RpcError extends Error {
|
|
116
124
|
constructor(readonly err_info: ErrorMap) {
|
|
@@ -224,6 +232,7 @@ class WsClient {
|
|
|
224
232
|
}
|
|
225
233
|
};
|
|
226
234
|
|
|
235
|
+
// eslint-disable-next-line complexity
|
|
227
236
|
const handleRequest = async (request: RpcRequest) => {
|
|
228
237
|
if ('onRequest' in this.options) {
|
|
229
238
|
const requestId = request.meta[RPC_MESSAGE_REQUEST_ID];
|
|
@@ -261,19 +270,77 @@ class WsClient {
|
|
|
261
270
|
|
|
262
271
|
try {
|
|
263
272
|
const shvPath = request.meta[RPC_MESSAGE_SHV_PATH];
|
|
264
|
-
const
|
|
273
|
+
const methodName = request.meta[RPC_MESSAGE_METHOD];
|
|
274
|
+
const unhandledMethod = () => {
|
|
275
|
+
throw new MethodNotFound(`No such path '${shvPath}' or method '${methodName}' or insufficient access rights.`);
|
|
276
|
+
};
|
|
277
|
+
|
|
265
278
|
const accessLevel = request.meta[RPC_MESSAGE_ACCESS_LEVEL];
|
|
266
279
|
if (accessLevel === undefined) {
|
|
267
|
-
|
|
280
|
+
unhandledMethod();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const methods = this.options.onRequest(shvPath);
|
|
285
|
+
if (methods === undefined) {
|
|
286
|
+
unhandledMethod();
|
|
287
|
+
return;
|
|
268
288
|
}
|
|
269
289
|
|
|
270
|
-
|
|
290
|
+
const methodDefinition = ((): {entry: DirEntry} & MethodHandler | undefined => {
|
|
291
|
+
const dirEntry = makeIMap({
|
|
292
|
+
[DIR_NAME]: 'dir',
|
|
293
|
+
[DIR_FLAGS]: 0,
|
|
294
|
+
[DIR_PARAM]: '',
|
|
295
|
+
[DIR_RESULT]: '',
|
|
296
|
+
[DIR_ACCESS]: accessLevelFromAccessString('rd'),
|
|
297
|
+
[DIR_SIGNALS]: makeMap({}),
|
|
298
|
+
[DIR_EXTRA]: makeMap({}),
|
|
299
|
+
});
|
|
300
|
+
switch (methodName) {
|
|
301
|
+
case 'dir':
|
|
302
|
+
return {
|
|
303
|
+
entry: dirEntry,
|
|
304
|
+
paramParser: DirParamZod,
|
|
305
|
+
handler(_shvPath, _method, param: DirParam) {
|
|
306
|
+
const methodsResult = [dirEntry, ...methods.map(method => method.entry)];
|
|
307
|
+
switch (true) {
|
|
308
|
+
case typeof param === 'string':
|
|
309
|
+
return methodsResult.find(method => method[DIR_NAME] === param) ?? false;
|
|
310
|
+
case typeof param === 'boolean':
|
|
311
|
+
case param === undefined:
|
|
312
|
+
console.log('methodsResult', '=', methodsResult);
|
|
313
|
+
return methodsResult;
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
default:
|
|
318
|
+
return methods.find(method => method.entry[DIR_NAME] === methodName);
|
|
319
|
+
}
|
|
320
|
+
})();
|
|
321
|
+
|
|
322
|
+
if (methodDefinition === undefined) {
|
|
323
|
+
unhandledMethod();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (accessLevel < methodDefinition.entry[DIR_ACCESS]) {
|
|
328
|
+
unhandledMethod();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const requestParam = request.value[RPC_MESSAGE_PARAMS];
|
|
333
|
+
const parsedParam = methodDefinition.paramParser.safeParse(requestParam);
|
|
334
|
+
if (!parsedParam.success) {
|
|
335
|
+
throw new InvalidParams(`Unexpected param for method ${methodName} on path ${shvPath}: ${toCpon(requestParam)}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let result = methodDefinition.handler(
|
|
271
339
|
shvPath,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
});
|
|
340
|
+
methodName,
|
|
341
|
+
parsedParam.data,
|
|
342
|
+
sendDelay,
|
|
343
|
+
);
|
|
277
344
|
if (result instanceof RequestHandler) {
|
|
278
345
|
this.requestHandlers.set(requestId, result);
|
|
279
346
|
result = result.options.result;
|