libshv-js 7.1.13 → 8.0.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/dist/access.js +1 -5
- package/dist/chainpack.d.ts +3 -3
- package/dist/chainpack.js +28 -35
- package/dist/cpcontext.js +1 -5
- package/dist/cpon.d.ts +2 -2
- package/dist/cpon.js +31 -40
- package/dist/index.d.ts +9 -9
- package/dist/index.js +9 -25
- package/dist/json.d.ts +1 -1
- package/dist/json.js +19 -24
- package/dist/rpcmessage.d.ts +1 -1
- package/dist/rpcmessage.js +19 -25
- package/dist/rpcvalue.js +2 -17
- package/dist/shv-ri.js +1 -5
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +2 -7
- package/dist/ws-client.d.ts +4 -3
- package/dist/ws-client.js +157 -160
- package/package.json +2 -1
package/dist/ws-client.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const rpcmessage_1 = require("./rpcmessage");
|
|
8
|
-
const rpcvalue_1 = require("./rpcvalue");
|
|
9
|
-
const utils_1 = require("./utils");
|
|
1
|
+
import { accessLevelFromAccessString } from './access.js';
|
|
2
|
+
import { ChainPackReader, CHAINPACK_PROTOCOL_TYPE, ChainPackWriter, toChainPack } from './chainpack.js';
|
|
3
|
+
import { CPON_PROTOCOL_TYPE, toCpon } from './cpon.js';
|
|
4
|
+
import { ERROR_MESSAGE, ErrorCode, ERROR_CODE, isSignal, isRequest, isResponse, ERROR_DATA, RPC_MESSAGE_METHOD, RPC_MESSAGE_SHV_PATH, RPC_MESSAGE_REQUEST_ID, RPC_MESSAGE_PARAMS, RPC_MESSAGE_ERROR, RPC_MESSAGE_RESULT, RPC_MESSAGE_CALLER_IDS, RPC_MESSAGE_DELAY, RPC_MESSAGE_ABORT, RPC_MESSAGE_ACCESS_LEVEL, RPC_MESSAGE_USER_ID } from './rpcmessage.js';
|
|
5
|
+
import { makeMap, makeIMap, RpcValueWithMetaData, makeMetaMap, Double } from './rpcvalue.js';
|
|
6
|
+
import { resolveRI } from './utils.js';
|
|
10
7
|
const DEFAULT_TIMEOUT = 5000;
|
|
11
8
|
const DEFAULT_PING_INTERVAL = 30 * 1000;
|
|
12
9
|
const defaultParseMessage = (rpcVal) => {
|
|
@@ -15,13 +12,13 @@ const defaultParseMessage = (rpcVal) => {
|
|
|
15
12
|
return rpcMsg;
|
|
16
13
|
};
|
|
17
14
|
const dataToRpcValue = (buff) => {
|
|
18
|
-
const rd = new
|
|
15
|
+
const rd = new ChainPackReader(buff);
|
|
19
16
|
rd.readUIntData();
|
|
20
17
|
const proto = rd.ctx.getByte();
|
|
21
18
|
switch (proto) {
|
|
22
|
-
case
|
|
19
|
+
case CHAINPACK_PROTOCOL_TYPE:
|
|
23
20
|
break;
|
|
24
|
-
case
|
|
21
|
+
case CPON_PROTOCOL_TYPE:
|
|
25
22
|
throw new Error('CPON protocol type is not supported anymore');
|
|
26
23
|
default:
|
|
27
24
|
throw new Error(`Unsupported protocol type ${proto}`);
|
|
@@ -29,19 +26,16 @@ const dataToRpcValue = (buff) => {
|
|
|
29
26
|
const rpcVal = rd.read();
|
|
30
27
|
return rpcVal;
|
|
31
28
|
};
|
|
32
|
-
class RequestHandler {
|
|
29
|
+
export class RequestHandler {
|
|
33
30
|
options;
|
|
34
31
|
constructor(options) {
|
|
35
32
|
this.options = options;
|
|
36
33
|
// nothing
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const validateDirParam = (param) => param === undefined || typeof param === 'string' || typeof param === 'boolean';
|
|
43
|
-
exports.validateDirParam = validateDirParam;
|
|
44
|
-
var DirFlags;
|
|
36
|
+
export const validateLsParam = (param) => param === undefined || typeof param === 'string';
|
|
37
|
+
export const validateDirParam = (param) => param === undefined || typeof param === 'string' || typeof param === 'boolean';
|
|
38
|
+
export var DirFlags;
|
|
45
39
|
(function (DirFlags) {
|
|
46
40
|
DirFlags[DirFlags["Reserved"] = 1] = "Reserved";
|
|
47
41
|
DirFlags[DirFlags["Getter"] = 2] = "Getter";
|
|
@@ -49,49 +43,49 @@ var DirFlags;
|
|
|
49
43
|
DirFlags[DirFlags["LargeResultHint"] = 8] = "LargeResultHint";
|
|
50
44
|
DirFlags[DirFlags["NotIdempotent"] = 16] = "NotIdempotent";
|
|
51
45
|
DirFlags[DirFlags["RequiresClientId"] = 32] = "RequiresClientId";
|
|
52
|
-
})(DirFlags || (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
})(DirFlags || (DirFlags = {}));
|
|
47
|
+
export const DIR_NAME = 1;
|
|
48
|
+
export const DIR_FLAGS = 2;
|
|
49
|
+
export const DIR_PARAM = 3;
|
|
50
|
+
export const DIR_RESULT = 4;
|
|
51
|
+
export const DIR_ACCESS = 5;
|
|
52
|
+
export const DIR_SIGNALS = 6;
|
|
53
|
+
export const DIR_EXTRA = 63;
|
|
60
54
|
class RpcError extends Error {
|
|
61
55
|
err_info;
|
|
62
56
|
constructor(err_info) {
|
|
63
57
|
const errorCodeToMessage = (code) => {
|
|
64
58
|
switch (code) {
|
|
65
|
-
case
|
|
66
|
-
case
|
|
67
|
-
case
|
|
68
|
-
case
|
|
69
|
-
case
|
|
70
|
-
case
|
|
71
|
-
case
|
|
72
|
-
case
|
|
73
|
-
case
|
|
74
|
-
case
|
|
75
|
-
case
|
|
76
|
-
case
|
|
59
|
+
case ErrorCode.InvalidRequest: return 'InvalidRequest';
|
|
60
|
+
case ErrorCode.MethodNotFound: return 'MethodNotFound';
|
|
61
|
+
case ErrorCode.InvalidParams: return 'InvalidParams';
|
|
62
|
+
case ErrorCode.InternalError: return 'InternalError';
|
|
63
|
+
case ErrorCode.ParseError: return 'ParseError';
|
|
64
|
+
case ErrorCode.MethodCallTimeout: return 'MethodCallTimeout';
|
|
65
|
+
case ErrorCode.MethodCallCancelled: return 'MethodCallCancelled';
|
|
66
|
+
case ErrorCode.MethodCallException: return 'MethodCallException';
|
|
67
|
+
case ErrorCode.Unknown: return 'Unknown';
|
|
68
|
+
case ErrorCode.LoginRequired: return 'LoginRequired';
|
|
69
|
+
case ErrorCode.UserIDRequired: return 'UserIDRequired';
|
|
70
|
+
case ErrorCode.NotImplemented: return 'NotImplemented';
|
|
77
71
|
default: return 'Unknown RpcError';
|
|
78
72
|
}
|
|
79
73
|
};
|
|
80
|
-
super(err_info[
|
|
74
|
+
super(err_info[ERROR_MESSAGE] ?? `${errorCodeToMessage(err_info[ERROR_CODE])} (${err_info[ERROR_CODE]})`);
|
|
81
75
|
this.err_info = err_info;
|
|
82
76
|
}
|
|
83
77
|
data() {
|
|
84
|
-
return this.err_info[
|
|
78
|
+
return this.err_info[ERROR_DATA];
|
|
85
79
|
}
|
|
86
80
|
}
|
|
87
81
|
const makeErrInfo = (errInfoOrMsg, code) => {
|
|
88
82
|
if (typeof errInfoOrMsg === 'object') {
|
|
89
83
|
return errInfoOrMsg;
|
|
90
84
|
}
|
|
91
|
-
return
|
|
92
|
-
[
|
|
93
|
-
[
|
|
94
|
-
[
|
|
85
|
+
return makeIMap({
|
|
86
|
+
[ERROR_CODE]: code,
|
|
87
|
+
[ERROR_MESSAGE]: errInfoOrMsg,
|
|
88
|
+
[ERROR_DATA]: undefined,
|
|
95
89
|
});
|
|
96
90
|
};
|
|
97
91
|
const createErrorClass = (code) => class extends RpcError {
|
|
@@ -100,24 +94,24 @@ const createErrorClass = (code) => class extends RpcError {
|
|
|
100
94
|
}
|
|
101
95
|
};
|
|
102
96
|
/* eslint-disable @typescript-eslint/naming-convention -- We want class names like this */
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
export const InvalidRequest = createErrorClass(ErrorCode.InvalidRequest);
|
|
98
|
+
export const MethodNotFound = createErrorClass(ErrorCode.MethodNotFound);
|
|
99
|
+
export const InvalidParams = createErrorClass(ErrorCode.InvalidParams);
|
|
100
|
+
export const InternalError = createErrorClass(ErrorCode.InternalError);
|
|
101
|
+
export const ParseError = createErrorClass(ErrorCode.ParseError);
|
|
102
|
+
export const MethodCallTimeout = createErrorClass(ErrorCode.MethodCallTimeout);
|
|
103
|
+
export const MethodCallCancelled = createErrorClass(ErrorCode.MethodCallCancelled);
|
|
104
|
+
export const MethodCallException = createErrorClass(ErrorCode.MethodCallException);
|
|
105
|
+
export const Unknown = createErrorClass(ErrorCode.Unknown);
|
|
106
|
+
export const LoginRequired = createErrorClass(ErrorCode.LoginRequired);
|
|
107
|
+
export const UserIDRequired = createErrorClass(ErrorCode.UserIDRequired);
|
|
108
|
+
export const NotImplemented = createErrorClass(ErrorCode.NotImplemented);
|
|
115
109
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
116
110
|
const makeTimeout = (ms, resolve) => setTimeout(() => {
|
|
117
|
-
resolve(new
|
|
118
|
-
[
|
|
119
|
-
[
|
|
120
|
-
[
|
|
111
|
+
resolve(new MethodCallTimeout(makeIMap({
|
|
112
|
+
[ERROR_CODE]: ErrorCode.MethodCallTimeout,
|
|
113
|
+
[ERROR_MESSAGE]: `Shv call timeout after: ${ms} msec.`,
|
|
114
|
+
[ERROR_DATA]: undefined,
|
|
121
115
|
})));
|
|
122
116
|
}, ms);
|
|
123
117
|
class RpcRequestPromise extends Promise {
|
|
@@ -155,10 +149,10 @@ class WsClient {
|
|
|
155
149
|
this.websocket.binaryType = 'arraybuffer';
|
|
156
150
|
const handleSignal = (signal) => {
|
|
157
151
|
for (const sub of this.subscriptions) {
|
|
158
|
-
const shvPath = signal.meta[
|
|
159
|
-
const signalName = signal.meta[
|
|
152
|
+
const shvPath = signal.meta[RPC_MESSAGE_SHV_PATH];
|
|
153
|
+
const signalName = signal.meta[RPC_MESSAGE_METHOD];
|
|
160
154
|
if (shvPath.startsWith(sub.path) && signalName === sub.signal) {
|
|
161
|
-
sub.callback(shvPath, signalName, signal.value[
|
|
155
|
+
sub.callback(shvPath, signalName, signal.value[RPC_MESSAGE_PARAMS]);
|
|
162
156
|
}
|
|
163
157
|
}
|
|
164
158
|
};
|
|
@@ -167,40 +161,40 @@ class WsClient {
|
|
|
167
161
|
if (!('onRequest' in this.options)) {
|
|
168
162
|
return;
|
|
169
163
|
}
|
|
170
|
-
const requestId = request.meta[
|
|
164
|
+
const requestId = request.meta[RPC_MESSAGE_REQUEST_ID];
|
|
171
165
|
const respond = (value) => {
|
|
172
|
-
this.sendRpcMessage(new
|
|
173
|
-
[
|
|
174
|
-
[
|
|
166
|
+
this.sendRpcMessage(new RpcValueWithMetaData(makeMetaMap({
|
|
167
|
+
[RPC_MESSAGE_CALLER_IDS]: request.meta[RPC_MESSAGE_CALLER_IDS],
|
|
168
|
+
[RPC_MESSAGE_REQUEST_ID]: requestId,
|
|
175
169
|
}), value));
|
|
176
170
|
};
|
|
177
171
|
const sendDelay = (progress) => {
|
|
178
|
-
respond(
|
|
179
|
-
[
|
|
172
|
+
respond(makeIMap({
|
|
173
|
+
[RPC_MESSAGE_DELAY]: new Double(progress),
|
|
180
174
|
}));
|
|
181
175
|
};
|
|
182
176
|
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
183
|
-
if (
|
|
177
|
+
if (RPC_MESSAGE_ABORT in request.value) {
|
|
184
178
|
const requestHandler = this.requestHandlers.get(requestId);
|
|
185
|
-
if (request.value[
|
|
179
|
+
if (request.value[RPC_MESSAGE_ABORT]) {
|
|
186
180
|
this.abortedRequests.add(requestId);
|
|
187
181
|
if (requestHandler?.options.onAbort !== undefined) {
|
|
188
182
|
requestHandler.options.onAbort();
|
|
189
183
|
}
|
|
190
184
|
}
|
|
191
|
-
if (!request.value[
|
|
185
|
+
if (!request.value[RPC_MESSAGE_ABORT] && requestHandler?.options.onProgressQuery !== undefined) {
|
|
192
186
|
const progress = requestHandler.options.onProgressQuery();
|
|
193
187
|
sendDelay(progress instanceof Promise ? await progress : progress);
|
|
194
188
|
}
|
|
195
189
|
return;
|
|
196
190
|
}
|
|
197
191
|
try {
|
|
198
|
-
const shvPath = request.meta[
|
|
199
|
-
const methodName = request.meta[
|
|
192
|
+
const shvPath = request.meta[RPC_MESSAGE_SHV_PATH];
|
|
193
|
+
const methodName = request.meta[RPC_MESSAGE_METHOD];
|
|
200
194
|
const unhandledMethod = () => {
|
|
201
|
-
throw new
|
|
195
|
+
throw new MethodNotFound(`No such path '${shvPath}' or method '${methodName}' or insufficient access rights.`);
|
|
202
196
|
};
|
|
203
|
-
const accessLevel = request.meta[
|
|
197
|
+
const accessLevel = request.meta[RPC_MESSAGE_ACCESS_LEVEL];
|
|
204
198
|
if (accessLevel === undefined) {
|
|
205
199
|
unhandledMethod();
|
|
206
200
|
return;
|
|
@@ -211,34 +205,34 @@ class WsClient {
|
|
|
211
205
|
return;
|
|
212
206
|
}
|
|
213
207
|
const methodDefinition = (() => {
|
|
214
|
-
const dirEntry =
|
|
215
|
-
[
|
|
216
|
-
[
|
|
217
|
-
[
|
|
218
|
-
[
|
|
219
|
-
[
|
|
220
|
-
[
|
|
221
|
-
[
|
|
208
|
+
const dirEntry = makeIMap({
|
|
209
|
+
[DIR_NAME]: 'dir',
|
|
210
|
+
[DIR_FLAGS]: 0,
|
|
211
|
+
[DIR_PARAM]: 'DirParam',
|
|
212
|
+
[DIR_RESULT]: 'DirResult',
|
|
213
|
+
[DIR_ACCESS]: accessLevelFromAccessString('bws'),
|
|
214
|
+
[DIR_SIGNALS]: makeMap({}),
|
|
215
|
+
[DIR_EXTRA]: makeMap({}),
|
|
222
216
|
});
|
|
223
|
-
const lsEntry =
|
|
224
|
-
[
|
|
225
|
-
[
|
|
226
|
-
[
|
|
227
|
-
[
|
|
228
|
-
[
|
|
229
|
-
[
|
|
230
|
-
[
|
|
217
|
+
const lsEntry = makeIMap({
|
|
218
|
+
[DIR_NAME]: 'ls',
|
|
219
|
+
[DIR_FLAGS]: 0,
|
|
220
|
+
[DIR_PARAM]: 'LsParam',
|
|
221
|
+
[DIR_RESULT]: 'LsResult',
|
|
222
|
+
[DIR_ACCESS]: accessLevelFromAccessString('bws'),
|
|
223
|
+
[DIR_SIGNALS]: makeMap({}),
|
|
224
|
+
[DIR_EXTRA]: makeMap({}),
|
|
231
225
|
});
|
|
232
226
|
switch (methodName) {
|
|
233
227
|
case 'dir':
|
|
234
228
|
return {
|
|
235
229
|
entry: dirEntry,
|
|
236
|
-
paramValidator:
|
|
230
|
+
paramValidator: validateDirParam,
|
|
237
231
|
handler(_shvPath, _method, param) {
|
|
238
232
|
const methodsResult = [dirEntry, lsEntry, ...methods.dirEntries.map(method => method.entry)];
|
|
239
233
|
switch (true) {
|
|
240
234
|
case typeof param === 'string':
|
|
241
|
-
return methodsResult.find(method => method[
|
|
235
|
+
return methodsResult.find(method => method[DIR_NAME] === param) ?? false;
|
|
242
236
|
case typeof param === 'boolean':
|
|
243
237
|
case param === undefined:
|
|
244
238
|
console.log('methodsResult', '=', methodsResult);
|
|
@@ -249,34 +243,34 @@ class WsClient {
|
|
|
249
243
|
case 'ls':
|
|
250
244
|
return {
|
|
251
245
|
entry: lsEntry,
|
|
252
|
-
paramValidator:
|
|
246
|
+
paramValidator: validateLsParam,
|
|
253
247
|
handler(shvPath) {
|
|
254
248
|
return methods.ls(shvPath);
|
|
255
249
|
},
|
|
256
250
|
};
|
|
257
251
|
default:
|
|
258
|
-
return methods.dirEntries.find(method => method.entry[
|
|
252
|
+
return methods.dirEntries.find(method => method.entry[DIR_NAME] === methodName);
|
|
259
253
|
}
|
|
260
254
|
})();
|
|
261
255
|
if (methodDefinition === undefined) {
|
|
262
256
|
unhandledMethod();
|
|
263
257
|
return;
|
|
264
258
|
}
|
|
265
|
-
if (accessLevel < methodDefinition.entry[
|
|
259
|
+
if (accessLevel < methodDefinition.entry[DIR_ACCESS]) {
|
|
266
260
|
unhandledMethod();
|
|
267
261
|
return;
|
|
268
262
|
}
|
|
269
|
-
const requestParam = request.value[
|
|
263
|
+
const requestParam = request.value[RPC_MESSAGE_PARAMS];
|
|
270
264
|
if (methodDefinition.paramValidator !== undefined && !methodDefinition.paramValidator(requestParam)) {
|
|
271
|
-
throw new
|
|
265
|
+
throw new InvalidParams(`Unexpected param '${shvPath}:${methodName}': ${toCpon(requestParam)}`);
|
|
272
266
|
}
|
|
273
267
|
let result = methodDefinition.handler(shvPath, methodName, requestParam, sendDelay);
|
|
274
268
|
if (result instanceof RequestHandler) {
|
|
275
269
|
this.requestHandlers.set(requestId, result);
|
|
276
270
|
result = result.options.result;
|
|
277
271
|
}
|
|
278
|
-
const resultOrError =
|
|
279
|
-
[
|
|
272
|
+
const resultOrError = makeIMap({
|
|
273
|
+
[RPC_MESSAGE_RESULT]: result instanceof Promise ? await result : result,
|
|
280
274
|
});
|
|
281
275
|
if (!this.abortedRequests.has(requestId)) {
|
|
282
276
|
respond(resultOrError);
|
|
@@ -290,8 +284,8 @@ class WsClient {
|
|
|
290
284
|
}
|
|
291
285
|
catch (error) {
|
|
292
286
|
const sendError = (error) => {
|
|
293
|
-
respond(
|
|
294
|
-
[
|
|
287
|
+
respond(makeIMap({
|
|
288
|
+
[RPC_MESSAGE_ERROR]: error.err_info,
|
|
295
289
|
}));
|
|
296
290
|
};
|
|
297
291
|
switch (true) {
|
|
@@ -299,54 +293,54 @@ class WsClient {
|
|
|
299
293
|
sendError(error);
|
|
300
294
|
break;
|
|
301
295
|
case error instanceof Error:
|
|
302
|
-
sendError(new
|
|
296
|
+
sendError(new InternalError(error.message));
|
|
303
297
|
break;
|
|
304
298
|
default:
|
|
305
|
-
sendError(new
|
|
299
|
+
sendError(new InternalError('Unknown error'));
|
|
306
300
|
break;
|
|
307
301
|
}
|
|
308
302
|
}
|
|
309
303
|
};
|
|
310
304
|
const handleResponse = (response) => {
|
|
311
|
-
const requestId = response.meta[
|
|
305
|
+
const requestId = response.meta[RPC_MESSAGE_REQUEST_ID];
|
|
312
306
|
if (this.rpcHandlers[requestId] !== undefined) {
|
|
313
307
|
const handler = this.rpcHandlers[requestId];
|
|
314
308
|
clearTimeout(handler.timeout_handle);
|
|
315
309
|
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
316
|
-
if (
|
|
310
|
+
if (RPC_MESSAGE_DELAY in response.value) {
|
|
317
311
|
if (handler.delayCallback !== undefined) {
|
|
318
|
-
handler.delayCallback(response.value[
|
|
312
|
+
handler.delayCallback(response.value[RPC_MESSAGE_DELAY].value);
|
|
319
313
|
}
|
|
320
314
|
handler.timeout_handle = makeTimeout(this.timeout, handler.resolve);
|
|
321
315
|
return;
|
|
322
316
|
}
|
|
323
317
|
handler.resolve((() => {
|
|
324
318
|
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
325
|
-
if (
|
|
326
|
-
const code = response.value[
|
|
319
|
+
if (RPC_MESSAGE_ERROR in response.value) {
|
|
320
|
+
const code = response.value[RPC_MESSAGE_ERROR][ERROR_CODE];
|
|
327
321
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
328
322
|
const ErrorTypeCtor = (() => {
|
|
329
323
|
switch (code) {
|
|
330
|
-
case
|
|
331
|
-
case
|
|
332
|
-
case
|
|
333
|
-
case
|
|
334
|
-
case
|
|
335
|
-
case
|
|
336
|
-
case
|
|
337
|
-
case
|
|
338
|
-
case
|
|
339
|
-
case
|
|
340
|
-
case
|
|
341
|
-
case
|
|
342
|
-
default: return
|
|
324
|
+
case ErrorCode.InvalidRequest: return InvalidRequest;
|
|
325
|
+
case ErrorCode.MethodNotFound: return MethodNotFound;
|
|
326
|
+
case ErrorCode.InvalidParams: return InvalidParams;
|
|
327
|
+
case ErrorCode.InternalError: return InternalError;
|
|
328
|
+
case ErrorCode.ParseError: return ParseError;
|
|
329
|
+
case ErrorCode.MethodCallTimeout: return MethodCallTimeout;
|
|
330
|
+
case ErrorCode.MethodCallCancelled: return MethodCallCancelled;
|
|
331
|
+
case ErrorCode.MethodCallException: return MethodCallException;
|
|
332
|
+
case ErrorCode.Unknown: return Unknown;
|
|
333
|
+
case ErrorCode.LoginRequired: return LoginRequired;
|
|
334
|
+
case ErrorCode.UserIDRequired: return UserIDRequired;
|
|
335
|
+
case ErrorCode.NotImplemented: return NotImplemented;
|
|
336
|
+
default: return Unknown;
|
|
343
337
|
}
|
|
344
338
|
})();
|
|
345
|
-
return new ErrorTypeCtor(response.value[
|
|
339
|
+
return new ErrorTypeCtor(response.value[RPC_MESSAGE_ERROR]);
|
|
346
340
|
}
|
|
347
341
|
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
348
|
-
if (
|
|
349
|
-
return response.value[
|
|
342
|
+
if (RPC_MESSAGE_RESULT in response.value) {
|
|
343
|
+
return response.value[RPC_MESSAGE_RESULT];
|
|
350
344
|
}
|
|
351
345
|
// At this point, the RpcResponse either has:
|
|
352
346
|
// - no keys, which means that the response has an implicit Null result.
|
|
@@ -354,7 +348,7 @@ class WsClient {
|
|
|
354
348
|
// https://silicon-heaven.github.io/shv-doc/rpcmessage.html#response
|
|
355
349
|
const rpcMessageKeys = Object.keys(response.value);
|
|
356
350
|
if (rpcMessageKeys.length !== 0) {
|
|
357
|
-
return new
|
|
351
|
+
return new NotImplemented(`Got an RpcResponse with unsupported keys: ${rpcMessageKeys.join(' ')}`);
|
|
358
352
|
}
|
|
359
353
|
})());
|
|
360
354
|
// eslint-disable-next-line @typescript-eslint/no-array-delete, @typescript-eslint/no-dynamic-delete
|
|
@@ -366,14 +360,14 @@ class WsClient {
|
|
|
366
360
|
const rpcVal = dataToRpcValue(evt.data);
|
|
367
361
|
const parseMessage = this.options.parseMessage ?? defaultParseMessage;
|
|
368
362
|
const rpcMsg = parseMessage(rpcVal);
|
|
369
|
-
this.logDebug(`message received: ${
|
|
370
|
-
if (
|
|
363
|
+
this.logDebug(`message received: ${toCpon(rpcMsg)}`);
|
|
364
|
+
if (isSignal(rpcMsg)) {
|
|
371
365
|
handleSignal(rpcMsg);
|
|
372
366
|
}
|
|
373
|
-
else if (
|
|
367
|
+
else if (isRequest(rpcMsg)) {
|
|
374
368
|
await handleRequest(rpcMsg);
|
|
375
369
|
}
|
|
376
|
-
else if (
|
|
370
|
+
else if (isResponse(rpcMsg)) {
|
|
377
371
|
handleResponse(rpcMsg);
|
|
378
372
|
}
|
|
379
373
|
});
|
|
@@ -433,10 +427,10 @@ class WsClient {
|
|
|
433
427
|
this.sendPing();
|
|
434
428
|
}, options.pingInterval ?? DEFAULT_PING_INTERVAL);
|
|
435
429
|
};
|
|
436
|
-
const makeLoginParams = (loginMap) =>
|
|
430
|
+
const makeLoginParams = (loginMap) => makeMap({
|
|
437
431
|
login: loginMap,
|
|
438
|
-
options:
|
|
439
|
-
device: typeof options.mountPoint === 'string' ?
|
|
432
|
+
options: makeMap({
|
|
433
|
+
device: typeof options.mountPoint === 'string' ? makeMap({ mountPoint: options.mountPoint }) : undefined,
|
|
440
434
|
}),
|
|
441
435
|
});
|
|
442
436
|
this.callRpcMethod(undefined, 'hello').then(response => {
|
|
@@ -447,13 +441,13 @@ class WsClient {
|
|
|
447
441
|
const makeLoginMap = () => {
|
|
448
442
|
switch (options.login.type) {
|
|
449
443
|
case 'PLAIN':
|
|
450
|
-
return
|
|
444
|
+
return makeMap({
|
|
451
445
|
password: options.login.password,
|
|
452
446
|
type: options.login.type,
|
|
453
447
|
user: options.login.user,
|
|
454
448
|
});
|
|
455
449
|
case 'TOKEN':
|
|
456
|
-
return
|
|
450
|
+
return makeMap({
|
|
457
451
|
token: options.login.token,
|
|
458
452
|
type: options.login.type,
|
|
459
453
|
});
|
|
@@ -473,16 +467,19 @@ class WsClient {
|
|
|
473
467
|
}
|
|
474
468
|
callRpcMethod(shvPath, method, params, options) {
|
|
475
469
|
const rqId = this.requestId++;
|
|
476
|
-
const makeRq = (value) => new
|
|
477
|
-
[
|
|
478
|
-
[
|
|
479
|
-
[
|
|
480
|
-
[
|
|
481
|
-
...(options?.requestUserId === true && { [
|
|
470
|
+
const makeRq = (value) => new RpcValueWithMetaData(makeMetaMap({
|
|
471
|
+
[RPC_MESSAGE_CALLER_IDS]: undefined,
|
|
472
|
+
[RPC_MESSAGE_REQUEST_ID]: rqId,
|
|
473
|
+
[RPC_MESSAGE_METHOD]: method ?? '',
|
|
474
|
+
[RPC_MESSAGE_SHV_PATH]: shvPath ?? '',
|
|
475
|
+
...(options?.requestUserId === true && { [RPC_MESSAGE_USER_ID]: '' }),
|
|
482
476
|
}), value);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
477
|
+
// We will not add an explicit null as a param unless the user requests it. Receivers of RpcCalls should
|
|
478
|
+
// generally handle a missing param the same as if the param was Null.
|
|
479
|
+
const paramRaw = params !== undefined || (options?.explicitNullParam ?? false) ? {
|
|
480
|
+
[RPC_MESSAGE_PARAMS]: params,
|
|
481
|
+
} : {};
|
|
482
|
+
let rq = makeRq(makeIMap(paramRaw));
|
|
486
483
|
if (options?.beforeSendHook !== undefined) {
|
|
487
484
|
rq = options.beforeSendHook(rq);
|
|
488
485
|
}
|
|
@@ -490,12 +487,12 @@ class WsClient {
|
|
|
490
487
|
const promise = new RpcRequestPromise(resolve => {
|
|
491
488
|
this.rpcHandlers[rqId] = { resolve, timeout_handle: makeTimeout(this.timeout, resolve), delayCallback: options?.delayCallback };
|
|
492
489
|
}, () => {
|
|
493
|
-
this.sendRpcMessage(makeRq(
|
|
494
|
-
[
|
|
490
|
+
this.sendRpcMessage(makeRq(makeIMap({
|
|
491
|
+
[RPC_MESSAGE_ABORT]: true,
|
|
495
492
|
})));
|
|
496
493
|
}, () => {
|
|
497
|
-
this.sendRpcMessage(makeRq(
|
|
498
|
-
[
|
|
494
|
+
this.sendRpcMessage(makeRq(makeIMap({
|
|
495
|
+
[RPC_MESSAGE_ABORT]: false,
|
|
499
496
|
})));
|
|
500
497
|
});
|
|
501
498
|
return promise;
|
|
@@ -504,16 +501,16 @@ class WsClient {
|
|
|
504
501
|
if (this.websocket.readyState !== WebSocket.OPEN) {
|
|
505
502
|
return;
|
|
506
503
|
}
|
|
507
|
-
this.logDebug('sending rpc message:',
|
|
508
|
-
const msgData = new Uint8Array(
|
|
509
|
-
const wr = new
|
|
504
|
+
this.logDebug('sending rpc message:', toCpon(rpcMsg));
|
|
505
|
+
const msgData = new Uint8Array(toChainPack(rpcMsg));
|
|
506
|
+
const wr = new ChainPackWriter();
|
|
510
507
|
wr.writeUIntData(msgData.length + 1);
|
|
511
508
|
const dgram = new Uint8Array(wr.ctx.length + 1 + msgData.length);
|
|
512
509
|
let ix = 0;
|
|
513
510
|
for (let i = 0; i < wr.ctx.length; i++) {
|
|
514
511
|
dgram[ix++] = wr.ctx.data[i];
|
|
515
512
|
}
|
|
516
|
-
dgram[ix++] =
|
|
513
|
+
dgram[ix++] = CHAINPACK_PROTOCOL_TYPE;
|
|
517
514
|
for (const msgDatum of msgData) {
|
|
518
515
|
dgram[ix++] = msgDatum;
|
|
519
516
|
}
|
|
@@ -536,7 +533,7 @@ class WsClient {
|
|
|
536
533
|
return this.shvApiVersion;
|
|
537
534
|
}
|
|
538
535
|
async subscribe(subscriber, riGetter, callback) {
|
|
539
|
-
const ri = await
|
|
536
|
+
const ri = await resolveRI(riGetter);
|
|
540
537
|
const path = ri.path();
|
|
541
538
|
const signal = ri.signal();
|
|
542
539
|
if (this.subscriptions.some(val => val.subscriber === subscriber && val.path === path && val.signal === signal)) {
|
|
@@ -547,7 +544,7 @@ class WsClient {
|
|
|
547
544
|
if (this.subscriptions.every(val => !(val.path === path && val.signal === signal))) {
|
|
548
545
|
switch (await this.getShvApiVersion()) {
|
|
549
546
|
case ShvApiVersion.V2: {
|
|
550
|
-
const result = await this.callRpcMethod('.broker/app', 'subscribe',
|
|
547
|
+
const result = await this.callRpcMethod('.broker/app', 'subscribe', makeMap({ signal, path }));
|
|
551
548
|
if (result instanceof Error) {
|
|
552
549
|
this.logDebug(`Couldn't subscribe to ${path}, ${signal}`);
|
|
553
550
|
}
|
|
@@ -575,7 +572,7 @@ class WsClient {
|
|
|
575
572
|
});
|
|
576
573
|
}
|
|
577
574
|
async unsubscribe(subscriber, riGetter) {
|
|
578
|
-
const ri = await
|
|
575
|
+
const ri = await resolveRI(riGetter);
|
|
579
576
|
const path = ri.path();
|
|
580
577
|
const signal = ri.signal();
|
|
581
578
|
const idx = this.subscriptions.findIndex(val => val.subscriber === subscriber && val.path === path && val.signal === signal);
|
|
@@ -590,14 +587,14 @@ class WsClient {
|
|
|
590
587
|
}
|
|
591
588
|
switch (await this.getShvApiVersion()) {
|
|
592
589
|
case ShvApiVersion.V2: {
|
|
593
|
-
const result = await this.callRpcMethod('.broker/app', 'unsubscribe',
|
|
590
|
+
const result = await this.callRpcMethod('.broker/app', 'unsubscribe', makeMap({ signal, path }));
|
|
594
591
|
if (result instanceof Error) {
|
|
595
592
|
this.logDebug(`Couldn't unsubscribe ${path}, ${signal}`);
|
|
596
593
|
}
|
|
597
594
|
break;
|
|
598
595
|
}
|
|
599
596
|
case ShvApiVersion.V3: {
|
|
600
|
-
const result = await this.callRpcMethod('.broker/currentClient', 'unsubscribe',
|
|
597
|
+
const result = await this.callRpcMethod('.broker/currentClient', 'unsubscribe', makeMap({ ri: ri.asString(), ttl: undefined }));
|
|
601
598
|
if (result instanceof Error) {
|
|
602
599
|
this.logDebug(`Couldn't unsubscribe ${path}, ${signal}`);
|
|
603
600
|
}
|
|
@@ -626,4 +623,4 @@ class WsClient {
|
|
|
626
623
|
this.options.logDebug(...args);
|
|
627
624
|
}
|
|
628
625
|
}
|
|
629
|
-
|
|
626
|
+
export { WsClient };
|
package/package.json
CHANGED