libshv-js 6.0.0 → 7.0.1
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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/shv-ri.d.ts +12 -0
- package/dist/shv-ri.js +55 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +8 -1
- package/dist/ws-client.d.ts +10 -4
- package/dist/ws-client.js +79 -26
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -21,4 +21,5 @@ __exportStar(require("./cpon"), exports);
|
|
|
21
21
|
__exportStar(require("./json"), exports);
|
|
22
22
|
__exportStar(require("./rpcmessage"), exports);
|
|
23
23
|
__exportStar(require("./rpcvalue"), exports);
|
|
24
|
+
__exportStar(require("./shv-ri"), exports);
|
|
24
25
|
__exportStar(require("./ws-client"), exports);
|
package/dist/shv-ri.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class ShvRI {
|
|
2
|
+
private ri;
|
|
3
|
+
private methodSepIx;
|
|
4
|
+
private signalSepIx?;
|
|
5
|
+
constructor(ri: string);
|
|
6
|
+
path(): string;
|
|
7
|
+
method(): string;
|
|
8
|
+
signal(): string | undefined;
|
|
9
|
+
hasSignal(): boolean;
|
|
10
|
+
asString(): string;
|
|
11
|
+
static fromPathMethodSignal(path: string, method: string, signal?: string): ShvRI;
|
|
12
|
+
}
|
package/dist/shv-ri.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShvRI = void 0;
|
|
4
|
+
class ShvRI {
|
|
5
|
+
ri;
|
|
6
|
+
methodSepIx;
|
|
7
|
+
signalSepIx;
|
|
8
|
+
constructor(ri) {
|
|
9
|
+
const methodSepIx = ri.indexOf(':');
|
|
10
|
+
if (methodSepIx === -1) {
|
|
11
|
+
throw new Error('Method separator ":" is missing.');
|
|
12
|
+
}
|
|
13
|
+
const signalSepRelative = ri.indexOf(':', methodSepIx + 1);
|
|
14
|
+
const signalSepIx = signalSepRelative !== -1 ? signalSepRelative : undefined;
|
|
15
|
+
this.ri = ri;
|
|
16
|
+
this.methodSepIx = methodSepIx;
|
|
17
|
+
this.signalSepIx = signalSepIx;
|
|
18
|
+
const method = this.method();
|
|
19
|
+
if (method.length === 0) {
|
|
20
|
+
throw new Error('Method must not be empty.');
|
|
21
|
+
}
|
|
22
|
+
const signal = this.signal();
|
|
23
|
+
if (signal?.length === 0) {
|
|
24
|
+
throw new Error('Signal, if present, must not be empty.');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
path() {
|
|
28
|
+
return this.ri.slice(0, this.methodSepIx);
|
|
29
|
+
}
|
|
30
|
+
method() {
|
|
31
|
+
if (this.signalSepIx !== undefined) {
|
|
32
|
+
return this.ri.slice(this.methodSepIx + 1, this.signalSepIx);
|
|
33
|
+
}
|
|
34
|
+
return this.ri.slice(this.methodSepIx + 1);
|
|
35
|
+
}
|
|
36
|
+
signal() {
|
|
37
|
+
if (this.signalSepIx !== undefined) {
|
|
38
|
+
return this.ri.slice(this.signalSepIx + 1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
hasSignal() {
|
|
42
|
+
return this.signalSepIx !== undefined;
|
|
43
|
+
}
|
|
44
|
+
asString() {
|
|
45
|
+
return this.ri;
|
|
46
|
+
}
|
|
47
|
+
static fromPathMethodSignal(path, method, signal) {
|
|
48
|
+
const m = method.length === 0 ? '*' : method;
|
|
49
|
+
const ri = signal !== undefined
|
|
50
|
+
? `${path}:${m}:${signal}`
|
|
51
|
+
: `${path}:${m}`;
|
|
52
|
+
return new ShvRI(ri);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.ShvRI = ShvRI;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
import { ShvRI } from './shv-ri';
|
|
1
2
|
export type StringGetter = string | Promise<string> | (() => string | Promise<string>);
|
|
2
3
|
export declare const resolveString: (input: StringGetter) => string | Promise<string>;
|
|
4
|
+
export type RIGetter = ShvRI | Promise<ShvRI> | (() => ShvRI | Promise<ShvRI>);
|
|
5
|
+
export declare const resolveRI: (input: RIGetter) => ShvRI | Promise<ShvRI>;
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveString = void 0;
|
|
3
|
+
exports.resolveRI = exports.resolveString = void 0;
|
|
4
4
|
const resolveString = (input) => {
|
|
5
5
|
if (typeof input === 'function') {
|
|
6
6
|
input = input();
|
|
@@ -8,3 +8,10 @@ const resolveString = (input) => {
|
|
|
8
8
|
return input;
|
|
9
9
|
};
|
|
10
10
|
exports.resolveString = resolveString;
|
|
11
|
+
const resolveRI = (input) => {
|
|
12
|
+
if (typeof input === 'function') {
|
|
13
|
+
input = input();
|
|
14
|
+
}
|
|
15
|
+
return input;
|
|
16
|
+
};
|
|
17
|
+
exports.resolveRI = resolveRI;
|
package/dist/ws-client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type RpcMessage, type ErrorMap } from './rpcmessage';
|
|
2
2
|
import { type RpcValue, type Null, type Int, type IMap, type ShvMap } from './rpcvalue';
|
|
3
|
-
import {
|
|
4
|
-
type SubscriptionCallback = (path: string,
|
|
3
|
+
import { RIGetter } from './utils';
|
|
4
|
+
type SubscriptionCallback = (path: string, signal: string, param?: RpcValue) => void;
|
|
5
5
|
type ResultOrError<T = RpcValue> = T | Error;
|
|
6
6
|
type Login = {
|
|
7
7
|
type: 'PLAIN';
|
|
@@ -209,6 +209,10 @@ export type CallRpcMethodOptions = {
|
|
|
209
209
|
delayCallback?: (progress: number) => void;
|
|
210
210
|
requestUserId?: boolean;
|
|
211
211
|
};
|
|
212
|
+
declare enum ShvApiVersion {
|
|
213
|
+
V2 = 0,
|
|
214
|
+
V3 = 1
|
|
215
|
+
}
|
|
212
216
|
declare class WsClient {
|
|
213
217
|
private requestId;
|
|
214
218
|
private pingTimerId;
|
|
@@ -219,6 +223,7 @@ declare class WsClient {
|
|
|
219
223
|
private readonly websocket;
|
|
220
224
|
private readonly options;
|
|
221
225
|
private readonly timeout;
|
|
226
|
+
private shvApiVersion;
|
|
222
227
|
constructor(options: WsClientOptions<MethodHandler>);
|
|
223
228
|
callRpcMethod(shvPath: '.broker/currentClient', method: 'accessGrantForMethodCall', params: [string, string], options?: CallRpcMethodOptions): RpcRequestPromise<ResultOrError<string>>;
|
|
224
229
|
callRpcMethod(shvPath: '.broker/currentClient', method: 'accessLevelForMethodCall', params: [string, string], options?: CallRpcMethodOptions): RpcRequestPromise<ResultOrError<number>>;
|
|
@@ -226,8 +231,9 @@ declare class WsClient {
|
|
|
226
231
|
callRpcMethod(shvPath: string | undefined, method: 'ls', params?: RpcValue, options?: CallRpcMethodOptions): RpcRequestPromise<ResultOrError<LsResult>>;
|
|
227
232
|
callRpcMethod(shvPath: string | undefined, method: string, params?: RpcValue, options?: CallRpcMethodOptions): RpcRequestPromise<ResultOrError>;
|
|
228
233
|
sendRpcMessage(rpcMsg: RpcMessage): void;
|
|
229
|
-
|
|
230
|
-
|
|
234
|
+
getShvApiVersion(): Promise<ShvApiVersion>;
|
|
235
|
+
subscribe(subscriber: string, riGetter: RIGetter, callback: SubscriptionCallback): Promise<void>;
|
|
236
|
+
unsubscribe(subscriber: string, riGetter: RIGetter): Promise<void>;
|
|
231
237
|
sendPing(): void;
|
|
232
238
|
accessGrantForMethodCall(path: string, method: string): RpcRequestPromise<ResultOrError<string>>;
|
|
233
239
|
accessLevelForMethodCall(path: string, method: string): RpcRequestPromise<ResultOrError<number>>;
|
package/dist/ws-client.js
CHANGED
|
@@ -129,6 +129,11 @@ class RpcRequestPromise extends Promise {
|
|
|
129
129
|
this.progressQuery = progressQuery;
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
var ShvApiVersion;
|
|
133
|
+
(function (ShvApiVersion) {
|
|
134
|
+
ShvApiVersion[ShvApiVersion["V2"] = 0] = "V2";
|
|
135
|
+
ShvApiVersion[ShvApiVersion["V3"] = 1] = "V3";
|
|
136
|
+
})(ShvApiVersion || (ShvApiVersion = {}));
|
|
132
137
|
class WsClient {
|
|
133
138
|
requestId = 1;
|
|
134
139
|
pingTimerId = undefined;
|
|
@@ -139,6 +144,7 @@ class WsClient {
|
|
|
139
144
|
websocket;
|
|
140
145
|
options;
|
|
141
146
|
timeout;
|
|
147
|
+
shvApiVersion = undefined;
|
|
142
148
|
constructor(options) {
|
|
143
149
|
if (typeof options !== 'object') {
|
|
144
150
|
throw new TypeError('No options object supplied');
|
|
@@ -150,9 +156,9 @@ class WsClient {
|
|
|
150
156
|
const handleSignal = (signal) => {
|
|
151
157
|
for (const sub of this.subscriptions) {
|
|
152
158
|
const shvPath = signal.meta[rpcmessage_1.RPC_MESSAGE_SHV_PATH];
|
|
153
|
-
const
|
|
154
|
-
if (shvPath.startsWith(sub.path) &&
|
|
155
|
-
sub.callback(shvPath,
|
|
159
|
+
const signalName = signal.meta[rpcmessage_1.RPC_MESSAGE_METHOD];
|
|
160
|
+
if (shvPath.startsWith(sub.path) && signalName === sub.signal) {
|
|
161
|
+
sub.callback(shvPath, signalName, signal.value[rpcmessage_1.RPC_MESSAGE_PARAMS]);
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
};
|
|
@@ -420,44 +426,91 @@ class WsClient {
|
|
|
420
426
|
this.websocket.send(dgram.buffer);
|
|
421
427
|
}
|
|
422
428
|
}
|
|
423
|
-
async
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
429
|
+
async getShvApiVersion() {
|
|
430
|
+
if (this.shvApiVersion === undefined) {
|
|
431
|
+
const lsResult = await this.callRpcMethod('.broker', 'ls');
|
|
432
|
+
if (lsResult instanceof Error) {
|
|
433
|
+
console.warn('Cannot detect SHV API version. Using version 2 as a fallback.');
|
|
434
|
+
this.shvApiVersion = ShvApiVersion.V2;
|
|
435
|
+
}
|
|
436
|
+
else if (lsResult.includes('client')) {
|
|
437
|
+
this.shvApiVersion = ShvApiVersion.V3;
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
this.shvApiVersion = ShvApiVersion.V2;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return this.shvApiVersion;
|
|
444
|
+
}
|
|
445
|
+
async subscribe(subscriber, riGetter, callback) {
|
|
446
|
+
const ri = await (0, utils_1.resolveRI)(riGetter);
|
|
447
|
+
const path = ri.path();
|
|
448
|
+
const signal = ri.signal();
|
|
449
|
+
if (this.subscriptions.some(val => val.subscriber === subscriber && val.path === path && val.signal === signal)) {
|
|
450
|
+
this.logDebug(`Already subscribed {$path}:${signal} for subscriber ${subscriber}`);
|
|
427
451
|
return;
|
|
428
452
|
}
|
|
429
|
-
// If this path:
|
|
430
|
-
if (!this.subscriptions.some(val => val.path === path && val.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
453
|
+
// If this path:signal has not been subscribed on the broker, do it now
|
|
454
|
+
if (!this.subscriptions.some(val => val.path === path && val.signal === signal)) {
|
|
455
|
+
switch (await this.getShvApiVersion()) {
|
|
456
|
+
case ShvApiVersion.V2: {
|
|
457
|
+
const result = await this.callRpcMethod('.broker/app', 'subscribe', (0, rpcvalue_1.makeMap)({ signal, path }));
|
|
458
|
+
if (result instanceof Error) {
|
|
459
|
+
this.logDebug(`Couldn't subscribe to ${path}, ${signal}`);
|
|
460
|
+
}
|
|
461
|
+
if (result instanceof Error) {
|
|
462
|
+
this.logDebug(`Couldn't subscribe to ${path}, ${signal}`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case ShvApiVersion.V3: {
|
|
468
|
+
const result = await this.callRpcMethod('.broker/currentClient', 'subscribe', [ri.asString(), undefined]);
|
|
469
|
+
if (result instanceof Error) {
|
|
470
|
+
this.logDebug(`Couldn't subscribe to ${path}, ${signal}`);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
436
476
|
}
|
|
437
477
|
this.subscriptions.push({
|
|
438
478
|
subscriber,
|
|
439
479
|
path,
|
|
440
|
-
|
|
480
|
+
signal,
|
|
441
481
|
callback,
|
|
442
482
|
});
|
|
443
483
|
}
|
|
444
|
-
async unsubscribe(subscriber,
|
|
445
|
-
const
|
|
446
|
-
const
|
|
484
|
+
async unsubscribe(subscriber, riGetter) {
|
|
485
|
+
const ri = await (0, utils_1.resolveRI)(riGetter);
|
|
486
|
+
const path = ri.path();
|
|
487
|
+
const signal = ri.signal();
|
|
488
|
+
const idx = this.subscriptions.findIndex(val => val.subscriber === subscriber && val.path === path && val.signal === signal);
|
|
447
489
|
if (idx === -1) {
|
|
448
|
-
this.logDebug(`No such subscription ${path}:${
|
|
490
|
+
this.logDebug(`No such subscription ${path}:${signal} for subscriber ${subscriber}`);
|
|
449
491
|
return;
|
|
450
492
|
}
|
|
451
493
|
this.subscriptions.splice(idx, 1);
|
|
452
|
-
// Unsubscribe on the broker only if there are no other subscriptions of this path:
|
|
453
|
-
if (this.subscriptions.some(val => val.path === path && val.
|
|
494
|
+
// Unsubscribe on the broker only if there are no other subscriptions of this path:signal
|
|
495
|
+
if (this.subscriptions.some(val => val.path === path && val.signal === signal)) {
|
|
454
496
|
return;
|
|
455
497
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
498
|
+
switch (await this.getShvApiVersion()) {
|
|
499
|
+
case ShvApiVersion.V2: {
|
|
500
|
+
const result = await this.callRpcMethod('.broker/app', 'unsubscribe', (0, rpcvalue_1.makeMap)({ signal, path }));
|
|
501
|
+
if (result instanceof Error) {
|
|
502
|
+
this.logDebug(`Couldn't unsubscribe ${path}, ${signal}`);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case ShvApiVersion.V3: {
|
|
507
|
+
const result = await this.callRpcMethod('.broker/currentClient', 'unsubscribe', (0, rpcvalue_1.makeMap)({ ri: ri.asString(), ttl: undefined }));
|
|
508
|
+
if (result instanceof Error) {
|
|
509
|
+
this.logDebug(`Couldn't unsubscribe ${path}, ${signal}`);
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
461
514
|
}
|
|
462
515
|
sendPing() {
|
|
463
516
|
this.callRpcMethod('.app', 'ping').catch((error) => {
|