libshv-js 3.5.0 → 3.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libshv-js",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "description": "Typescript implementation of libshv",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
package/src/chainpack.ts CHANGED
@@ -85,7 +85,7 @@ const significantBitsCount = (num: bigint) => {
85
85
  class ChainPackReader {
86
86
  ctx: UnpackContext;
87
87
 
88
- constructor(data: ArrayBuffer | Uint8Array | UnpackContext) {
88
+ constructor(data: ArrayBuffer | UnpackContext) {
89
89
  this.ctx = data instanceof UnpackContext ? data : new UnpackContext(data);
90
90
  }
91
91
 
package/src/cpcontext.ts CHANGED
@@ -2,7 +2,7 @@ class UnpackContext {
2
2
  index: number;
3
3
  private readonly data: Uint8Array;
4
4
 
5
- constructor(buf: ArrayBuffer) {
5
+ constructor(buf: ArrayBufferLike) {
6
6
  this.data = new Uint8Array(buf);
7
7
  this.index = 0;
8
8
  }
package/src/ws-client.ts CHANGED
@@ -36,21 +36,38 @@ type Subscription = {
36
36
  callback: SubscriptionCallback;
37
37
  };
38
38
 
39
- type WsClientOptions = {
39
+ type Login = {
40
+ type: 'PLAIN';
41
+ user: string;
42
+ password: string;
43
+ } | {
44
+ type: 'TOKEN';
45
+ token: string;
46
+ };
47
+
48
+ type WsClientOptionsCommon = {
40
49
  logDebug: (...args: string[]) => void;
50
+ wsUri: string;
51
+ };
52
+
53
+ type WsClientOptionsLogin = WsClientOptionsCommon & {
41
54
  mountPoint?: string;
42
- user?: string;
43
- password: string;
44
- loginType?: 'PLAIN' | 'AZURE';
55
+ login: Login;
45
56
  timeout?: number;
46
57
  pingInterval?: number;
47
- wsUri: string;
48
58
  onConnected: () => void;
49
59
  onConnectionFailure: (error: Error) => void;
50
60
  onDisconnected: () => void;
51
61
  onRequest: (rpc_msg: RpcRequest) => void;
52
62
  };
53
63
 
64
+ type WsClientOptionsWorkflows = WsClientOptionsCommon & {
65
+ onWorkflows: (workflows: RpcValue) => void;
66
+ onWorkflowsFailed: () => void;
67
+ };
68
+
69
+ type WsClientOptions = WsClientOptionsLogin | WsClientOptionsWorkflows;
70
+
54
71
  type LsResult = string[];
55
72
  export enum DirFlags {
56
73
  Reserved = 1,
@@ -103,99 +120,31 @@ class UserIDRequired extends RpcError {}
103
120
  class NotImplemented extends RpcError {}
104
121
 
105
122
  class WsClient {
106
- requestId = 1;
107
- pingTimerId = -1;
123
+ private requestId = 1;
124
+ private pingTimerId = -1;
108
125
 
109
- rpcHandlers: Array<{
126
+ private rpcHandlers: Array<{
110
127
  resolve: RpcResponseResolver;
111
128
  timeout_handle: number;
112
129
  }> = [];
113
130
 
114
- subscriptions: Subscription[] = [];
115
- websocket: WebSocket;
116
-
117
- logDebug: WsClientOptions['logDebug'];
118
- mountPoint: WsClientOptions['mountPoint'];
119
- user?: WsClientOptions['user'];
120
- password: WsClientOptions['password'];
121
- loginType: WsClientOptions['loginType'];
122
- onConnected: WsClientOptions['onConnected'];
123
- onConnectionFailure: WsClientOptions['onConnectionFailure'];
124
- onDisconnected: WsClientOptions['onDisconnected'];
125
- onRequest: WsClientOptions['onRequest'];
126
- timeout: WsClientOptions['timeout'];
127
- pingInterval: WsClientOptions['pingInterval'];
131
+ private readonly subscriptions: Subscription[] = [];
132
+ private readonly websocket: WebSocket;
133
+
134
+ private readonly options: WsClientOptions;
135
+ private readonly timeout: number;
128
136
 
129
137
  constructor(options: WsClientOptions) {
130
138
  if (typeof options !== 'object') {
131
139
  throw new TypeError('No options object supplied');
132
140
  }
133
141
 
134
- this.logDebug = options.logDebug ?? (() => {/* nothing */});
135
- this.mountPoint = options.mountPoint;
136
-
137
- this.user = options.user ?? '';
138
- this.password = options.password;
139
- this.loginType = options.loginType ?? 'PLAIN';
142
+ this.options = options;
143
+ this.timeout = 'timeout' in options && options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT;
140
144
 
141
145
  this.websocket = new WebSocket(options.wsUri);
142
146
  this.websocket.binaryType = 'arraybuffer';
143
147
 
144
- this.pingInterval = options.pingInterval ?? DEFAULT_PING_INTERVAL;
145
- this.onConnected = options.onConnected ?? (() => {/* nothing */});
146
- this.onConnectionFailure = options.onConnectionFailure ?? (() => {/* nothing */});
147
- this.onDisconnected = options.onDisconnected ?? (() => {/* nothing */});
148
- this.onRequest = options.onRequest ?? (() => {/* nothing */});
149
-
150
- this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
151
-
152
- this.websocket.addEventListener('open', () => {
153
- this.logDebug('CONNECTED');
154
- const handleConnectionError = (error: Error) => {
155
- this.logDebug('FAILURE: couldn\'t perform initial handshake', error.message);
156
- this.onConnectionFailure(error);
157
- };
158
-
159
- this.callRpcMethod(undefined, 'hello').then(response => {
160
- if (response instanceof Error) {
161
- handleConnectionError(response);
162
- return;
163
- }
164
-
165
- const params = makeMap({
166
- login: makeMap({
167
- password: this.password,
168
- type: this.loginType,
169
- user: this.user,
170
- }),
171
- options: makeMap({
172
- device: this.mountPoint === 'string' ? makeMap({mountPoint: this.mountPoint}) : undefined,
173
- }),
174
- });
175
- return this.callRpcMethod(undefined, 'login', params);
176
- }).then(response => {
177
- if (response instanceof Error) {
178
- handleConnectionError(response);
179
- return;
180
- }
181
-
182
- this.logDebug('SUCCESS: connected to shv broker');
183
- this.onConnected();
184
- this.pingTimerId = window.setInterval(() => {
185
- this.sendPing();
186
- }, this.pingInterval);
187
- }).catch(() => {
188
- this.logDebug('FAILURE: couldn\' connected to shv broker');
189
- });
190
- });
191
-
192
- this.websocket.addEventListener('close', () => {
193
- this.logDebug('DISCONNECTED');
194
- this.subscriptions.length = 0;
195
- this.onDisconnected();
196
- window.clearInterval(this.pingTimerId);
197
- });
198
-
199
148
  this.websocket.addEventListener('message', (evt: MessageEvent<ArrayBuffer>) => {
200
149
  const rpcVal = dataToRpcValue(evt.data);
201
150
  const rpcMsg = RpcMessageZod.parse(rpcVal);
@@ -211,7 +160,9 @@ class WsClient {
211
160
  }
212
161
  }
213
162
  } else if (isRequest(rpcMsg)) {
214
- this.onRequest(rpcMsg);
163
+ if ('onRequest' in this.options) {
164
+ this.options.onRequest(rpcMsg);
165
+ }
215
166
  } else if (isResponse(rpcMsg)) {
216
167
  const requestId = rpcMsg.meta[RPC_MESSAGE_REQUEST_ID];
217
168
 
@@ -258,6 +209,12 @@ class WsClient {
258
209
  console.log('WebSocket ERROR', evt);
259
210
  this.logDebug('WebSocket ERROR');
260
211
  });
212
+
213
+ if ('onWorkflows' in this.options) {
214
+ this.workflowsProcedure(this.options);
215
+ } else {
216
+ this.loginProcedure(this.options);
217
+ }
261
218
  }
262
219
 
263
220
  callRpcMethod(shv_path: '.broker/currentClient', method: 'accessGrantForMethodCall', params: [string, string]): Promise<ResultOrError<string>>;
@@ -272,8 +229,7 @@ class WsClient {
272
229
  [RPC_MESSAGE_SHV_PATH]: shv_path ?? '',
273
230
  }), makeIMap({
274
231
  [RPC_MESSAGE_PARAMS]: params,
275
- }),
276
- );
232
+ }));
277
233
 
278
234
  this.sendRpcMessage(rq);
279
235
 
@@ -370,6 +326,110 @@ class WsClient {
370
326
  close() {
371
327
  this.websocket.close();
372
328
  }
329
+
330
+ private logDebug(...args: string[]) {
331
+ if (this.options.logDebug === undefined) {
332
+ return;
333
+ }
334
+
335
+ this.options.logDebug(...args);
336
+ }
337
+
338
+ private workflowsProcedure(options: WsClientOptionsWorkflows) {
339
+ let workflowsObtained = false;
340
+ this.websocket.addEventListener('open', () => {
341
+ this.options.logDebug('CONNECTED');
342
+ const handleConnectionError = (error: Error) => {
343
+ this.logDebug('FAILURE: couldn\'t retrieve workflows', error.message);
344
+ options.onWorkflowsFailed();
345
+ };
346
+
347
+ this.callRpcMethod(undefined, 'workflows').then(response => {
348
+ if (response instanceof Error) {
349
+ handleConnectionError(response);
350
+ return;
351
+ }
352
+
353
+ workflowsObtained = true;
354
+ options.onWorkflows(response);
355
+
356
+ this.close();
357
+ }).catch((_: unknown) => {
358
+ options.onWorkflowsFailed();
359
+ });
360
+ });
361
+
362
+ this.websocket.addEventListener('close', () => {
363
+ this.logDebug('DISCONNECTED');
364
+ if (!workflowsObtained) {
365
+ options.onWorkflowsFailed();
366
+ }
367
+ });
368
+ }
369
+
370
+ private loginProcedure(options: WsClientOptionsLogin) {
371
+ this.websocket.addEventListener('open', () => {
372
+ this.options.logDebug('CONNECTED');
373
+ const handleConnectionError = (error: Error) => {
374
+ this.logDebug('FAILURE: couldn\'t perform initial handshake', error.message);
375
+ options.onConnectionFailure(error);
376
+ };
377
+
378
+ const handleLoginResponse = (response: any) => {
379
+ if (response instanceof Error) {
380
+ handleConnectionError(response);
381
+ return;
382
+ }
383
+
384
+ this.logDebug('SUCCESS: connected to shv broker');
385
+ options.onConnected();
386
+ this.pingTimerId = window.setInterval(() => {
387
+ this.sendPing();
388
+ }, options.pingInterval ?? DEFAULT_PING_INTERVAL);
389
+ };
390
+
391
+ const makeLoginParams = (loginMap: ShvMap) => makeMap({
392
+ login: loginMap,
393
+ options: makeMap({
394
+ device: options.mountPoint === 'string' ? makeMap({mountPoint: options.mountPoint}) : undefined,
395
+ }),
396
+ });
397
+
398
+ this.callRpcMethod(undefined, 'hello').then(response => {
399
+ if (response instanceof Error) {
400
+ handleConnectionError(response);
401
+ return;
402
+ }
403
+
404
+ const makeLoginMap = () => {
405
+ switch (options.login.type) {
406
+ case 'PLAIN':
407
+ return makeMap({
408
+ password: options.login.password,
409
+ type: options.login.type,
410
+ user: options.login.user,
411
+ });
412
+ case 'TOKEN':
413
+ return makeMap({
414
+ token: options.login.token,
415
+ type: options.login.type,
416
+ });
417
+ }
418
+ };
419
+
420
+ return this.callRpcMethod(undefined, 'login', makeLoginParams(makeLoginMap()));
421
+ }).then(handleLoginResponse).catch(() => {
422
+ this.logDebug('FAILURE: couldn\' connected to shv broker');
423
+ });
424
+ });
425
+
426
+ this.websocket.addEventListener('close', () => {
427
+ this.logDebug('DISCONNECTED');
428
+ this.subscriptions.length = 0;
429
+ options.onDisconnected();
430
+ window.clearInterval(this.pingTimerId);
431
+ });
432
+ }
373
433
  }
374
434
 
375
435
  export {WsClient};