libshv-js 3.13.0 → 3.15.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 +113 -26
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,24 @@ 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
|
+
|
|
68
|
+
export type DirEntry = IMap<{
|
|
69
|
+
[DIR_NAME]: string;
|
|
70
|
+
[DIR_FLAGS]: number;
|
|
71
|
+
[DIR_PARAM]: string | Null;
|
|
72
|
+
[DIR_RESULT]: string | Null;
|
|
73
|
+
[DIR_ACCESS]: Int;
|
|
74
|
+
[DIR_SIGNALS]: ShvMap<Record<string, string | Null>>;
|
|
75
|
+
[DIR_EXTRA]: ShvMap;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
export type DirResult = DirEntry[];
|
|
79
|
+
|
|
80
|
+
export type MethodHandler = {
|
|
81
|
+
paramParser: z.ZodType<RpcValue>,
|
|
82
|
+
handler: (shvPath: string, method: string, params: RpcValue, delay: (progress: number) => void) => Promise<RpcValue> | RpcValue | RequestHandler,
|
|
69
83
|
};
|
|
70
84
|
|
|
71
85
|
type WsClientOptionsLogin = WsClientOptionsCommon & {
|
|
@@ -76,7 +90,10 @@ type WsClientOptionsLogin = WsClientOptionsCommon & {
|
|
|
76
90
|
onConnected: () => void;
|
|
77
91
|
onConnectionFailure: (error: Error) => void;
|
|
78
92
|
onDisconnected: () => void;
|
|
79
|
-
onRequest: (
|
|
93
|
+
onRequest: (shvPath: string) => {
|
|
94
|
+
ls: (shvPath: string) => string[] | Promise<string[]>;
|
|
95
|
+
dirEntries: Array<{entry: DirEntry} & MethodHandler>;
|
|
96
|
+
} | 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];
|
|
@@ -246,9 +255,12 @@ class WsClient {
|
|
|
246
255
|
if (RPC_MESSAGE_ABORT in request.value) {
|
|
247
256
|
const requestHandler = this.requestHandlers.get(requestId);
|
|
248
257
|
|
|
249
|
-
if (request.value[RPC_MESSAGE_ABORT] === true
|
|
258
|
+
if (request.value[RPC_MESSAGE_ABORT] === true) {
|
|
250
259
|
this.abortedRequests.add(requestId);
|
|
251
|
-
|
|
260
|
+
|
|
261
|
+
if (requestHandler?.options.onAbort !== undefined) {
|
|
262
|
+
requestHandler.options.onAbort();
|
|
263
|
+
}
|
|
252
264
|
}
|
|
253
265
|
|
|
254
266
|
if (request.value[RPC_MESSAGE_ABORT] === false && requestHandler?.options.onProgressQuery !== undefined) {
|
|
@@ -261,19 +273,94 @@ class WsClient {
|
|
|
261
273
|
|
|
262
274
|
try {
|
|
263
275
|
const shvPath = request.meta[RPC_MESSAGE_SHV_PATH];
|
|
264
|
-
const
|
|
276
|
+
const methodName = request.meta[RPC_MESSAGE_METHOD];
|
|
277
|
+
const unhandledMethod = () => {
|
|
278
|
+
throw new MethodNotFound(`No such path '${shvPath}' or method '${methodName}' or insufficient access rights.`);
|
|
279
|
+
};
|
|
280
|
+
|
|
265
281
|
const accessLevel = request.meta[RPC_MESSAGE_ACCESS_LEVEL];
|
|
266
282
|
if (accessLevel === undefined) {
|
|
267
|
-
|
|
283
|
+
unhandledMethod();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const methods = this.options.onRequest(shvPath);
|
|
288
|
+
if (methods === undefined) {
|
|
289
|
+
unhandledMethod();
|
|
290
|
+
return;
|
|
268
291
|
}
|
|
269
292
|
|
|
270
|
-
|
|
293
|
+
const methodDefinition = ((): {entry: DirEntry} & MethodHandler | undefined => {
|
|
294
|
+
const dirEntry = makeIMap({
|
|
295
|
+
[DIR_NAME]: 'dir',
|
|
296
|
+
[DIR_FLAGS]: 0,
|
|
297
|
+
[DIR_PARAM]: 'DirParam',
|
|
298
|
+
[DIR_RESULT]: 'DirResult',
|
|
299
|
+
[DIR_ACCESS]: accessLevelFromAccessString('bws'),
|
|
300
|
+
[DIR_SIGNALS]: makeMap({}),
|
|
301
|
+
[DIR_EXTRA]: makeMap({}),
|
|
302
|
+
});
|
|
303
|
+
const lsEntry = makeIMap({
|
|
304
|
+
[DIR_NAME]: 'ls',
|
|
305
|
+
[DIR_FLAGS]: 0,
|
|
306
|
+
[DIR_PARAM]: 'LsParam',
|
|
307
|
+
[DIR_RESULT]: 'LsResult',
|
|
308
|
+
[DIR_ACCESS]: accessLevelFromAccessString('bws'),
|
|
309
|
+
[DIR_SIGNALS]: makeMap({}),
|
|
310
|
+
[DIR_EXTRA]: makeMap({}),
|
|
311
|
+
});
|
|
312
|
+
switch (methodName) {
|
|
313
|
+
case 'dir':
|
|
314
|
+
return {
|
|
315
|
+
entry: dirEntry,
|
|
316
|
+
paramParser: DirParamZod,
|
|
317
|
+
handler(_shvPath, _method, param) {
|
|
318
|
+
const methodsResult = [dirEntry, lsEntry, ...methods.dirEntries.map(method => method.entry)];
|
|
319
|
+
switch (true) {
|
|
320
|
+
case typeof param === 'string':
|
|
321
|
+
return methodsResult.find(method => method[DIR_NAME] === param) ?? false;
|
|
322
|
+
case typeof param === 'boolean':
|
|
323
|
+
case param === undefined:
|
|
324
|
+
console.log('methodsResult', '=', methodsResult);
|
|
325
|
+
return methodsResult;
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
case 'ls':
|
|
330
|
+
return {
|
|
331
|
+
entry: lsEntry,
|
|
332
|
+
paramParser: LsParamZod,
|
|
333
|
+
handler(shvPath) {
|
|
334
|
+
return methods.ls(shvPath);
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
default:
|
|
338
|
+
return methods.dirEntries.find(method => method.entry[DIR_NAME] === methodName);
|
|
339
|
+
}
|
|
340
|
+
})();
|
|
341
|
+
|
|
342
|
+
if (methodDefinition === undefined) {
|
|
343
|
+
unhandledMethod();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (accessLevel < methodDefinition.entry[DIR_ACCESS]) {
|
|
348
|
+
unhandledMethod();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const requestParam = request.value[RPC_MESSAGE_PARAMS];
|
|
353
|
+
const parsedParam = methodDefinition.paramParser.safeParse(requestParam);
|
|
354
|
+
if (!parsedParam.success) {
|
|
355
|
+
throw new InvalidParams(`Unexpected param for method ${methodName} on path ${shvPath}: ${toCpon(requestParam)}`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
let result = methodDefinition.handler(
|
|
271
359
|
shvPath,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
});
|
|
360
|
+
methodName,
|
|
361
|
+
parsedParam.data,
|
|
362
|
+
sendDelay,
|
|
363
|
+
);
|
|
277
364
|
if (result instanceof RequestHandler) {
|
|
278
365
|
this.requestHandlers.set(requestId, result);
|
|
279
366
|
result = result.options.result;
|