camstreamerlib 3.1.0 → 3.2.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/CreatePackage.js CHANGED
@@ -4,16 +4,19 @@ const AdmZip = require("adm-zip");
4
4
  const Path = require("path");
5
5
  const fs = require("fs");
6
6
  const child_process_1 = require("child_process");
7
+ const productionModulesFolder = 'production_modules';
7
8
  function isDirectory(path) {
8
9
  const stat = fs.statSync(path);
9
10
  return stat.isDirectory();
10
11
  }
11
- const productionModulesFolder = 'production_modules';
12
- function getPackageVersion(folder) {
12
+ function getPackageInfo(folder) {
13
13
  try {
14
14
  const manifest = fs.readFileSync(Path.join(folder, 'manifest.json'));
15
15
  const manifestParsed = JSON.parse(manifest.toString());
16
- return manifestParsed.package_version.replace(/\./g, '_');
16
+ return {
17
+ packageName: manifestParsed.package_name,
18
+ packageVersion: manifestParsed.package_version.replace(/\./g, '_'),
19
+ };
17
20
  }
18
21
  catch (err) {
19
22
  console.error('Get package version:', err);
@@ -81,8 +84,12 @@ function main(args) {
81
84
  options.typeScriptPackage = true;
82
85
  }
83
86
  const folder = Path.resolve('.');
84
- const packageVersion = getPackageVersion(folder);
85
- const zipFile = `${options.outputFolder}/${Path.basename(folder)}_${packageVersion}.zip`;
87
+ const packageInfo = getPackageInfo(folder);
88
+ if (packageInfo === undefined) {
89
+ console.error('Package info not found');
90
+ process.exit(1);
91
+ }
92
+ const zipFile = `${options.outputFolder}/${packageInfo.packageName}_${packageInfo.packageVersion}.zip`;
86
93
  if (fs.existsSync(zipFile)) {
87
94
  try {
88
95
  fs.unlinkSync(zipFile);
package/DefaultAgent.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
- import { Options, IClient } from './internal/common';
3
+ import { IClient, HttpOptions } from './internal/common';
4
4
  export declare class DefaultAgent implements IClient {
5
5
  private tls;
6
- private tlsInsecure;
7
6
  private ip;
8
7
  private port;
9
8
  private user;
10
9
  private pass;
11
- constructor(opt?: Options);
12
- private getBaseConnectionParams;
10
+ private httpRequestSender;
11
+ constructor(opt?: HttpOptions);
13
12
  get(path: string, parameters?: Record<string, string>, headers?: Record<string, string>): Promise<Response>;
14
13
  post(path: string, data: string | Buffer | FormData, parameters?: Record<string, string>, headers?: Record<string, string>): Promise<Response>;
14
+ private getBaseConnectionParams;
15
15
  }
package/DefaultAgent.js CHANGED
@@ -10,22 +10,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DefaultAgent = void 0;
13
- const HttpRequest_1 = require("./internal/HttpRequest");
14
- function isBrowserEnvironment() {
15
- return typeof process === 'undefined' || process.versions === null || process.versions.node === null;
16
- }
13
+ const HttpRequestSender_1 = require("./internal/HttpRequestSender");
17
14
  class DefaultAgent {
18
15
  constructor(opt = {}) {
19
- var _a, _b, _c, _d, _e, _f;
16
+ var _a, _b, _c, _d, _e;
20
17
  this.tls = (_a = opt.tls) !== null && _a !== void 0 ? _a : false;
21
- this.tlsInsecure = (_b = opt.tlsInsecure) !== null && _b !== void 0 ? _b : false;
22
- this.ip = (_c = opt.ip) !== null && _c !== void 0 ? _c : '127.0.0.1';
23
- this.port = (_d = opt.port) !== null && _d !== void 0 ? _d : (this.tls ? 443 : 80);
24
- this.user = (_e = opt.user) !== null && _e !== void 0 ? _e : '';
25
- this.pass = (_f = opt.pass) !== null && _f !== void 0 ? _f : '';
26
- if (isBrowserEnvironment() && opt.tlsInsecure) {
27
- throw new Error("HTTPS insecure can't be used on the frontend side.");
18
+ this.ip = (_b = opt.ip) !== null && _b !== void 0 ? _b : '127.0.0.1';
19
+ this.port = (_c = opt.port) !== null && _c !== void 0 ? _c : (this.tls ? 443 : 80);
20
+ this.user = (_d = opt.user) !== null && _d !== void 0 ? _d : '';
21
+ this.pass = (_e = opt.pass) !== null && _e !== void 0 ? _e : '';
22
+ let agentOptions;
23
+ if (opt.tlsInsecure !== undefined || opt.keepAlive !== undefined) {
24
+ agentOptions = {
25
+ rejectUnaurhorized: !opt.tlsInsecure,
26
+ keepAlive: opt.keepAlive,
27
+ };
28
28
  }
29
+ this.httpRequestSender = new HttpRequestSender_1.HttpRequestSender(agentOptions);
30
+ }
31
+ get(path, parameters = {}, headers) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const options = this.getBaseConnectionParams('GET', path, parameters);
34
+ options.headers = headers;
35
+ return this.httpRequestSender.sendRequest(options);
36
+ });
37
+ }
38
+ post(path, data, parameters = {}, headers) {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ const options = this.getBaseConnectionParams('POST', path, parameters);
41
+ options.headers = headers;
42
+ return this.httpRequestSender.sendRequest(options, data);
43
+ });
29
44
  }
30
45
  getBaseConnectionParams(method, path, params) {
31
46
  if (path.indexOf('?') === -1) {
@@ -46,22 +61,7 @@ class DefaultAgent {
46
61
  path: path,
47
62
  user: this.user,
48
63
  pass: this.pass,
49
- rejectUnauthorized: !this.tlsInsecure,
50
64
  };
51
65
  }
52
- get(path, parameters = {}, headers) {
53
- return __awaiter(this, void 0, void 0, function* () {
54
- const options = this.getBaseConnectionParams('GET', path, parameters);
55
- options.headers = headers;
56
- return (0, HttpRequest_1.sendRequest)(options);
57
- });
58
- }
59
- post(path, data, parameters = {}, headers) {
60
- return __awaiter(this, void 0, void 0, function* () {
61
- const options = this.getBaseConnectionParams('POST', path, parameters);
62
- options.headers = headers;
63
- return (0, HttpRequest_1.sendRequest)(options, data);
64
- });
65
- }
66
66
  }
67
67
  exports.DefaultAgent = DefaultAgent;
package/README.md CHANGED
@@ -14,13 +14,23 @@ npm install camstreamerlib
14
14
  # Documentation for Node.js modules
15
15
 
16
16
  - [HttpServer](doc/HttpServer.md) is a module for processing HTTP requests in your scripts. It also automatically serves up the content from html directory or you can register paths which you can process by your own (e.g. http://$CAMERA_IP/local/camscripter/proxy/$MY_PACKAGE_NAME/control.cgi).
17
+
17
18
  - [CameraVapix](doc/CameraVapix.md) is a module to access Axis camera VAPIX interface.
19
+
18
20
  - [CamStreamerAPI](doc/CamStreamerAPI.md) is a module for easy control of video streaming in the CamStreamer ACAP application (RTMP, HLS, SRT and MPEG-TS protocols).
21
+
19
22
  - [CamOverlayAPI](doc/CamOverlayAPI.md) is a module to access CamOverlay HTTP interface.
23
+
20
24
  - [CamOverlayDrawingAPI](doc/CamOverlayDrawingAPI.md) is a module for easy control of CamOverlay drawing API. For more details on supported video overlay drawing functions see https://camstreamer.com/camoverlay-api1
21
- - [CamScripterAPICameraEventsGenerator](doc/CamScripterAPICameraEventsGenerator.md) is a module which allows generating events on an Axis camera. These events can be used for triggers in the Axis camera rule engine (events/actions). It is also an easy way how to integrate events and metadata in VMS systems which support Axis camera events.
25
+
22
26
  - [CamOverlayPainter/](doc/CamOverlayPainter.md) contains three modules which makes easier to use CamOverlayDrawingAPI.
23
27
 
28
+ - [CamScripterAPICameraEventsGenerator](doc/CamScripterAPICameraEventsGenerator.md) is a module which allows generating events on an Axis camera. These events can be used for triggers in the Axis camera rule engine (events/actions). It is also an easy way how to integrate events and metadata in VMS systems which support Axis camera events.
29
+
30
+ - [CamSwitcherEvents](doc/CamSwitcherEvents.md) is a module which allows receiving events from CamSwitcher ACAP application.
31
+
32
+ - [VapixEvents](doc/VapixEvents.md) is a module which allows receiving camera events from the VAPIX API.
33
+
24
34
  ## For Developers
25
35
 
26
36
  ### Publishing to npm repository
package/VapixEvents.d.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import { EventEmitter2 as EventEmitter } from 'eventemitter2';
2
- import { Options } from './internal/common';
3
- export type VapixEventsOptions = Options;
2
+ import { WsOptions } from './internal/common';
3
+ export type VapixEventsOptions = WsOptions;
4
4
  export declare class VapixEvents extends EventEmitter {
5
- private ws?;
6
5
  private tls;
7
6
  private tlsInsecure;
8
7
  private ip;
9
8
  private port;
10
9
  private user;
11
10
  private pass;
11
+ private ws;
12
12
  constructor(options?: VapixEventsOptions);
13
13
  connect(): void;
14
14
  disconnect(): void;
15
+ private createWsClient;
15
16
  private isReservedEventName;
16
17
  }
package/VapixEvents.js CHANGED
@@ -13,12 +13,16 @@ class VapixEvents extends eventemitter2_1.EventEmitter2 {
13
13
  this.port = (_d = options.port) !== null && _d !== void 0 ? _d : (this.tls ? 443 : 80);
14
14
  this.user = (_e = options.user) !== null && _e !== void 0 ? _e : 'root';
15
15
  this.pass = (_f = options.pass) !== null && _f !== void 0 ? _f : '';
16
+ this.createWsClient();
16
17
  eventemitter2_1.EventEmitter2.call(this);
17
18
  }
18
19
  connect() {
19
- if (this.ws !== undefined) {
20
- throw new Error('Websocket is already opened.');
21
- }
20
+ this.ws.open();
21
+ }
22
+ disconnect() {
23
+ this.ws.close();
24
+ }
25
+ createWsClient() {
22
26
  const options = {
23
27
  tls: this.tls,
24
28
  tlsInsecure: this.tlsInsecure,
@@ -30,7 +34,6 @@ class VapixEvents extends eventemitter2_1.EventEmitter2 {
30
34
  };
31
35
  this.ws = new WsClient_1.WsClient(options);
32
36
  this.ws.on('open', () => {
33
- var _a;
34
37
  const topics = [];
35
38
  const eventNames = this.eventNames();
36
39
  for (let i = 0; i < eventNames.length; i++) {
@@ -48,16 +51,16 @@ class VapixEvents extends eventemitter2_1.EventEmitter2 {
48
51
  eventFilterList: topics,
49
52
  },
50
53
  };
51
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(topicFilter));
54
+ this.ws.send(JSON.stringify(topicFilter));
52
55
  });
53
56
  this.ws.on('message', (data) => {
54
57
  const dataJSON = JSON.parse(data.toString());
55
58
  if (dataJSON.method === 'events:configure') {
56
59
  if (dataJSON.error === undefined) {
57
- this.emit('eventsConnect');
60
+ this.emit('open');
58
61
  }
59
62
  else {
60
- this.emit('eventsDisconnect', dataJSON.error);
63
+ this.emit('error', dataJSON.error);
61
64
  this.disconnect();
62
65
  }
63
66
  return;
@@ -66,24 +69,14 @@ class VapixEvents extends eventemitter2_1.EventEmitter2 {
66
69
  this.emit(eventName, dataJSON);
67
70
  });
68
71
  this.ws.on('error', (error) => {
69
- this.emit('eventsDisconnect', error);
70
- this.ws = undefined;
72
+ this.emit('error', error);
71
73
  });
72
74
  this.ws.on('close', () => {
73
- if (this.ws !== undefined) {
74
- this.emit('eventsClose');
75
- }
76
- this.ws = undefined;
75
+ this.emit('close');
77
76
  });
78
- this.ws.open();
79
- }
80
- disconnect() {
81
- if (this.ws !== undefined) {
82
- this.ws.close();
83
- }
84
77
  }
85
78
  isReservedEventName(eventName) {
86
- return eventName === 'eventsConnect' || eventName === 'eventsDisconnect' || eventName === 'eventsClose';
79
+ return eventName === 'open' || eventName === 'close' || eventName === 'error';
87
80
  }
88
81
  }
89
82
  exports.VapixEvents = VapixEvents;
@@ -1,5 +1,5 @@
1
- import { IClient, Options } from '../internal/common';
2
- export type AcsEventsOptions = Options;
1
+ import { IClient, HttpOptions } from '../internal/common';
2
+ export type AcsEventsOptions = HttpOptions;
3
3
  export declare class AxisCameraStationEvents {
4
4
  private sourceKey;
5
5
  private client;
@@ -1,3 +1,4 @@
1
1
  export declare class Digest {
2
- static getAuthHeader(user: string, pass: string, method: string, uri: string, wwwAuthenticateHeader: string): string;
2
+ private nonceCount;
3
+ getAuthHeader(user: string, pass: string, method: string, uri: string, wwwAuthenticateHeader: string): string;
3
4
  }
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Digest = void 0;
4
4
  const crypto = require("crypto");
5
5
  class Digest {
6
- static getAuthHeader(user, pass, method, uri, wwwAuthenticateHeader) {
6
+ constructor() {
7
+ this.nonceCount = 1;
8
+ }
9
+ getAuthHeader(user, pass, method, uri, wwwAuthenticateHeader) {
7
10
  const digestItems = {};
8
11
  const digestArr = wwwAuthenticateHeader.substring(wwwAuthenticateHeader.indexOf('Digest') + 6).split(',');
9
12
  for (let i = 0; i < digestArr.length; i++) {
@@ -14,11 +17,12 @@ class Digest {
14
17
  }
15
18
  const HA1 = crypto.createHash('md5').update(`${user}:${digestItems['realm']}:${pass}`).digest('hex');
16
19
  const HA2 = crypto.createHash('md5').update(`${method}:${uri}`).digest('hex');
20
+ const ncValue = ('00000000' + this.nonceCount.toString(16)).slice(-8);
17
21
  let response;
18
22
  if (digestItems['qop'] !== undefined) {
19
23
  response = crypto
20
24
  .createHash('md5')
21
- .update(`${HA1}:${digestItems['nonce']}:00000001:162d50aa594e9648:auth:${HA2}`)
25
+ .update(`${HA1}:${digestItems['nonce']}:${ncValue}:162d50aa594e9648:auth:${HA2}`)
22
26
  .digest('hex');
23
27
  }
24
28
  else {
@@ -31,8 +35,9 @@ class Digest {
31
35
  `uri="${uri}",` +
32
36
  `response="${response}"`;
33
37
  if (digestItems['qop'] !== undefined) {
34
- header += ',qop=auth,nc=00000001,cnonce="162d50aa594e9648"';
38
+ header += `,qop=auth,nc=${ncValue},cnonce="162d50aa594e9648"`;
35
39
  }
40
+ this.nonceCount++;
36
41
  return header;
37
42
  }
38
43
  }
@@ -0,0 +1,28 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ export type HttpRequestOptions = {
4
+ method?: string;
5
+ protocol: string;
6
+ host: string;
7
+ port: number;
8
+ path: string;
9
+ user?: string;
10
+ pass?: string;
11
+ timeout?: number;
12
+ headers?: Record<string, string>;
13
+ };
14
+ export type AgentOptions = {
15
+ rejectUnaurhorized?: boolean;
16
+ keepAlive?: boolean;
17
+ };
18
+ export declare class HttpRequestSender {
19
+ private agent?;
20
+ private authData?;
21
+ constructor(agentOptions?: AgentOptions);
22
+ sendRequest(options: HttpRequestOptions, postData?: Buffer | string | FormData): Promise<Response>;
23
+ private sendRequestWithAuth;
24
+ private static getURL;
25
+ private getAuthorization;
26
+ private invalidateAuthorization;
27
+ private static getAuthHeader;
28
+ }
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.HttpRequestSender = void 0;
13
+ const Digest_1 = require("./Digest");
14
+ const undici_1 = require("undici");
15
+ const common_1 = require("./common");
16
+ class HttpRequestSender {
17
+ constructor(agentOptions) {
18
+ if ((0, common_1.isBrowserEnvironment)()) {
19
+ if (agentOptions !== undefined) {
20
+ throw new Error('Agent options can be specified only in Node.js environment.');
21
+ }
22
+ }
23
+ else {
24
+ this.agent = new undici_1.Agent({
25
+ connect: { rejectUnauthorized: agentOptions === null || agentOptions === void 0 ? void 0 : agentOptions.rejectUnaurhorized, keepAlive: agentOptions === null || agentOptions === void 0 ? void 0 : agentOptions.keepAlive },
26
+ });
27
+ }
28
+ }
29
+ sendRequest(options, postData) {
30
+ return this.sendRequestWithAuth(options, postData);
31
+ }
32
+ sendRequestWithAuth(options, postData, wwwAuthenticateHeader) {
33
+ var _a;
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ const url = HttpRequestSender.getURL(options);
36
+ const controller = new AbortController();
37
+ if (options.timeout !== undefined) {
38
+ setTimeout(() => controller.abort(new Error('Request timeout')), options.timeout);
39
+ }
40
+ const authorization = this.getAuthorization(options, wwwAuthenticateHeader);
41
+ if (authorization !== undefined) {
42
+ (_a = options.headers) !== null && _a !== void 0 ? _a : (options.headers = {});
43
+ options.headers['Authorization'] = authorization;
44
+ }
45
+ let res;
46
+ if (this.agent) {
47
+ const req = new undici_1.Request(url, { body: postData, method: options.method, headers: options.headers });
48
+ res = yield (0, undici_1.fetch)(req, { signal: controller.signal, dispatcher: this.agent });
49
+ }
50
+ else {
51
+ const req = new Request(url, { body: postData, method: options.method, headers: options.headers });
52
+ res = yield fetch(req, { signal: controller.signal });
53
+ }
54
+ if (!res.ok) {
55
+ this.invalidateAuthorization();
56
+ }
57
+ if (res.status === 401) {
58
+ const authenticateHeader = res.headers.get('www-authenticate');
59
+ if (authenticateHeader !== null &&
60
+ authenticateHeader.indexOf('Digest') !== -1 &&
61
+ wwwAuthenticateHeader === undefined) {
62
+ return this.sendRequestWithAuth(options, postData, authenticateHeader);
63
+ }
64
+ else {
65
+ return res;
66
+ }
67
+ }
68
+ else {
69
+ return res;
70
+ }
71
+ });
72
+ }
73
+ static getURL(options) {
74
+ const url = new URL(`${options.protocol}//${options.host}:${options.port}${options.path}`);
75
+ return url.toString();
76
+ }
77
+ getAuthorization(options, wwwAuthenticateHeader) {
78
+ if (options.user === undefined || options.pass === undefined) {
79
+ this.authData = undefined;
80
+ return;
81
+ }
82
+ if (this.authData &&
83
+ (this.authData.host !== options.host ||
84
+ this.authData.port !== options.port ||
85
+ this.authData.user !== options.user ||
86
+ this.authData.pass !== options.pass ||
87
+ (wwwAuthenticateHeader !== undefined && this.authData.wwwAuthenticateHeader !== wwwAuthenticateHeader))) {
88
+ this.authData = undefined;
89
+ }
90
+ if (this.authData === undefined) {
91
+ this.authData = {
92
+ host: options.host,
93
+ port: options.port,
94
+ user: options.user,
95
+ pass: options.pass,
96
+ wwwAuthenticateHeader,
97
+ digest: new Digest_1.Digest(),
98
+ };
99
+ }
100
+ return HttpRequestSender.getAuthHeader(options, this.authData);
101
+ }
102
+ invalidateAuthorization() {
103
+ this.authData = undefined;
104
+ }
105
+ static getAuthHeader(options, authData) {
106
+ var _a;
107
+ if (options.user === undefined || options.pass === undefined) {
108
+ throw new Error('No credentials found');
109
+ }
110
+ if (authData.wwwAuthenticateHeader !== undefined && authData.wwwAuthenticateHeader.indexOf('Digest') !== -1) {
111
+ return authData.digest.getAuthHeader(options.user, options.pass, (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', options.path, authData.wwwAuthenticateHeader);
112
+ }
113
+ else {
114
+ return `Basic ${btoa(options.user + ':' + options.pass)}`;
115
+ }
116
+ }
117
+ }
118
+ exports.HttpRequestSender = HttpRequestSender;
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  import * as EventEmitter from 'events';
4
- import { Options } from './common';
5
- export type WsClientOptions = Options & {
4
+ import { WsOptions } from './common';
5
+ export type WsClientOptions = WsOptions & {
6
6
  address: string;
7
7
  headers?: Record<string, string>;
8
8
  pingInterval?: number;
@@ -19,9 +19,10 @@ export declare class WsClient extends EventEmitter {
19
19
  private isAlive;
20
20
  private pingTimer?;
21
21
  private ws?;
22
+ private isClosed;
22
23
  constructor(options: WsClientOptions);
23
- open(digestHeader?: string): void;
24
+ open(wwwAuthenticateHeader?: string): void;
24
25
  send(data: Buffer | string): void;
25
26
  close(): void;
26
- private handleCloseEvent;
27
+ private closeWsConnection;
27
28
  }
@@ -1,14 +1,25 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.WsClient = void 0;
4
13
  const EventEmitter = require("events");
5
14
  const WebSocket = require("ws");
15
+ const timersPromises = require("timers/promises");
6
16
  const Digest_1 = require("./Digest");
7
17
  class WsClient extends EventEmitter {
8
18
  constructor(options) {
9
19
  var _a, _b, _c, _d, _e, _f, _g, _h;
10
20
  super();
11
21
  this.isAlive = true;
22
+ this.isClosed = false;
12
23
  const tls = (_a = options === null || options === void 0 ? void 0 : options.tls) !== null && _a !== void 0 ? _a : false;
13
24
  const tlsInsecure = (_b = options === null || options === void 0 ? void 0 : options.tlsInsecure) !== null && _b !== void 0 ? _b : false;
14
25
  const ip = (_c = options === null || options === void 0 ? void 0 : options.ip) !== null && _c !== void 0 ? _c : '127.0.0.1';
@@ -26,7 +37,11 @@ class WsClient extends EventEmitter {
26
37
  headers: (_h = options.headers) !== null && _h !== void 0 ? _h : {},
27
38
  };
28
39
  }
29
- open(digestHeader) {
40
+ open(wwwAuthenticateHeader) {
41
+ if (this.ws !== undefined) {
42
+ return;
43
+ }
44
+ this.isClosed = false;
30
45
  if (this.protocol === undefined) {
31
46
  this.ws = new WebSocket(this.address, this.wsOptions);
32
47
  }
@@ -35,39 +50,42 @@ class WsClient extends EventEmitter {
35
50
  }
36
51
  this.ws.binaryType = 'arraybuffer';
37
52
  this.isAlive = true;
38
- this.pingTimer = setInterval(() => {
53
+ this.pingTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
39
54
  var _a;
40
55
  if ((this.ws && this.ws.readyState !== WebSocket.OPEN) || this.isAlive === false) {
41
56
  this.emit('error', new Error('Connection timeout'));
42
- this.close();
57
+ yield this.closeWsConnection();
43
58
  }
44
59
  else {
45
60
  this.isAlive = false;
46
61
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.ping();
47
62
  }
48
- }, this.pingInterval);
63
+ }), this.pingInterval);
49
64
  this.ws.on('pong', () => {
50
65
  this.isAlive = true;
51
66
  });
52
- if (digestHeader !== undefined) {
53
- this.wsOptions.headers['Authorization'] = Digest_1.Digest.getAuthHeader(this.user, this.pass, 'GET', this.digestAddress, digestHeader);
67
+ if (wwwAuthenticateHeader !== undefined) {
68
+ this.wsOptions.headers['Authorization'] = new Digest_1.Digest().getAuthHeader(this.user, this.pass, 'GET', this.digestAddress, wwwAuthenticateHeader);
54
69
  }
55
- this.ws.on('unexpected-response', (req, res) => {
70
+ this.ws.on('unexpected-response', (req, res) => __awaiter(this, void 0, void 0, function* () {
71
+ var _b;
56
72
  if (res.statusCode === 401 && res.headers['www-authenticate'] !== undefined) {
57
73
  if (this.pingTimer) {
58
74
  clearInterval(this.pingTimer);
59
75
  }
76
+ (_b = this.ws) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
77
+ this.ws = undefined;
60
78
  this.open(res.headers['www-authenticate']);
61
79
  }
62
80
  else {
63
- const e = new Error('Status code: ' + res.statusCode);
64
- this.emit('error', e);
81
+ this.emit('error', new Error('Status code: ' + res.statusCode));
82
+ yield this.closeWsConnection();
65
83
  }
66
- });
84
+ }));
67
85
  this.ws.on('open', () => this.emit('open'));
68
86
  this.ws.on('message', (data) => this.emit('message', data));
69
87
  this.ws.on('error', (error) => this.emit('error', error));
70
- this.ws.on('close', () => this.handleCloseEvent());
88
+ this.ws.on('close', () => this.closeWsConnection());
71
89
  }
72
90
  send(data) {
73
91
  if (this.ws === undefined) {
@@ -78,30 +96,45 @@ class WsClient extends EventEmitter {
78
96
  }
79
97
  }
80
98
  close() {
81
- if (this.ws === undefined) {
82
- throw new Error("This websocket hasn't been opened yet.");
83
- }
84
- try {
85
- this.handleCloseEvent();
86
- if (this.ws.readyState !== WebSocket.CLOSING && this.ws.readyState !== WebSocket.CLOSED) {
87
- this.ws.close();
88
- }
89
- }
90
- catch (err) {
99
+ if (this.isClosed) {
100
+ return;
91
101
  }
102
+ this.isClosed = true;
103
+ const currentWs = this.ws;
104
+ this.closeWsConnection().catch((err) => {
105
+ console.error(err);
106
+ });
92
107
  setTimeout(() => {
93
- if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
94
- this.ws.terminate();
108
+ if (currentWs && currentWs.readyState !== WebSocket.CLOSED) {
109
+ currentWs.terminate();
95
110
  }
96
111
  }, 5000);
97
112
  }
98
- handleCloseEvent() {
99
- var _a;
100
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
101
- if (this.pingTimer) {
102
- clearInterval(this.pingTimer);
103
- }
104
- this.emit('close');
113
+ closeWsConnection() {
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ try {
116
+ if (this.ws === undefined) {
117
+ return;
118
+ }
119
+ this.ws.removeAllListeners();
120
+ if (this.pingTimer) {
121
+ clearInterval(this.pingTimer);
122
+ }
123
+ if (this.ws.readyState !== WebSocket.CONNECTING &&
124
+ this.ws.readyState !== WebSocket.CLOSING &&
125
+ this.ws.readyState !== WebSocket.CLOSED) {
126
+ this.ws.close();
127
+ }
128
+ this.ws = undefined;
129
+ this.emit('close');
130
+ if (!this.isClosed) {
131
+ yield timersPromises.setTimeout(10000);
132
+ yield this.open();
133
+ }
134
+ }
135
+ catch (err) {
136
+ }
137
+ });
105
138
  }
106
139
  }
107
140
  exports.WsClient = WsClient;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
- export type Options = {
3
+ type Options = {
4
4
  ip?: string;
5
5
  port?: number;
6
6
  user?: string;
@@ -8,6 +8,10 @@ export type Options = {
8
8
  tls?: boolean;
9
9
  tlsInsecure?: boolean;
10
10
  };
11
+ export type HttpOptions = Options & {
12
+ keepAlive?: boolean;
13
+ };
14
+ export type WsOptions = Options;
11
15
  export type TGetFunction = (url: string, parameters?: Record<string, string>, headers?: Record<string, string>) => Promise<Response>;
12
16
  export type TPostFunction = (url: string, data: string | Buffer | FormData, parameters?: Record<string, string>, headers?: Record<string, string>) => Promise<Response>;
13
17
  export interface IClient {
@@ -15,3 +19,5 @@ export interface IClient {
15
19
  post: TPostFunction;
16
20
  }
17
21
  export declare function isClient(arg?: Options | IClient): arg is IClient;
22
+ export declare function isBrowserEnvironment(): boolean;
23
+ export {};