videomail-client 16.6.0 → 16.7.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.
@@ -7212,7 +7212,7 @@ var __webpack_modules__ = {
7212
7212
  const qs = __webpack_require__("b");
7213
7213
  const RequestBase = __webpack_require__("d");
7214
7214
  const { isObject, mixin, hasOwn, isSafeKey } = __webpack_require__("Dp");
7215
- const ResponseBase = __webpack_require__("es");
7215
+ const ResponseBase = __webpack_require__("e");
7216
7216
  const Agent = __webpack_require__("pf");
7217
7217
  function noop() {}
7218
7218
  module.exports = function(method, url) {
@@ -7926,7 +7926,7 @@ var __webpack_modules__ = {
7926
7926
  }, this._responseTimeout);
7927
7927
  };
7928
7928
  },
7929
- es (module, __unused_rspack_exports, __webpack_require__) {
7929
+ e (module, __unused_rspack_exports, __webpack_require__) {
7930
7930
  "use strict";
7931
7931
  const utils = __webpack_require__("Dp");
7932
7932
  module.exports = ResponseBase;
@@ -10637,7 +10637,7 @@ var __webpack_modules__ = {
10637
10637
  return i[1];
10638
10638
  };
10639
10639
  },
10640
- R (module) {
10640
+ RR (module) {
10641
10641
  "use strict";
10642
10642
  var stylesInDOM = [];
10643
10643
  function getIndexByIdentifier(identifier) {
@@ -11138,7 +11138,7 @@ var __webpack_exports__ = {};
11138
11138
  var client = __webpack_require__("Q");
11139
11139
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11140
11140
  var package_namespaceObject = {
11141
- rE: "16.6.0"
11141
+ rE: "16.7.0"
11142
11142
  };
11143
11143
  function isAudioEnabled(options) {
11144
11144
  return Boolean(options.audio.enabled);
@@ -15855,7 +15855,7 @@ var __webpack_exports__ = {};
15855
15855
  return newOptions;
15856
15856
  }
15857
15857
  const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
15858
- var injectStylesIntoStyleTag = __webpack_require__("R");
15858
+ var injectStylesIntoStyleTag = __webpack_require__("RR");
15859
15859
  var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
15860
15860
  var styleDomAPI = __webpack_require__("Bd");
15861
15861
  var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
@@ -19303,6 +19303,18 @@ var __webpack_exports__ = {};
19303
19303
  canvas_to_buffer_modern_r.atob = void 0;
19304
19304
  var websocket_stream_stream = __webpack_require__("U");
19305
19305
  var stream_default = /*#__PURE__*/ __webpack_require__.n(websocket_stream_stream);
19306
+ function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19307
+ return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19308
+ }
19309
+ const util_isAutomatedUserAgent = isAutomatedUserAgent;
19310
+ function getAutomationSignals() {
19311
+ const webdriver = navigator.webdriver;
19312
+ return {
19313
+ webdriver: true === webdriver,
19314
+ userAgentMatched: util_isAutomatedUserAgent()
19315
+ };
19316
+ }
19317
+ const util_getAutomationSignals = getAutomationSignals;
19306
19318
  function getEdgeCodes(value, amount, fromEnd) {
19307
19319
  const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
19308
19320
  const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
@@ -19330,15 +19342,16 @@ var __webpack_exports__ = {};
19330
19342
  if (languageList.length > 0) return languageList.join(",");
19331
19343
  return navigator.language || "none";
19332
19344
  }
19333
- function getAutomationSignals(probeBase, probeSimpleQuery) {
19345
+ function getDiagnosticSignals(probeBase, probeSimpleQuery) {
19334
19346
  const reasons = [];
19335
- if (navigator.webdriver) reasons.push("webdriver=true");
19336
- if (/HeadlessChrome|PhantomJS|puppeteer|playwright/iu.test(navigator.userAgent)) reasons.push("headless_ua_marker");
19347
+ const automation = util_getAutomationSignals();
19348
+ if (automation.webdriver) reasons.push("webdriver=true");
19349
+ if (automation.userAgentMatched) reasons.push("headless_ua_marker");
19337
19350
  if (0 === navigator.languages.length) reasons.push("no_languages");
19338
19351
  if ("ok" !== probeBase) reasons.push("probe_base_failed");
19339
19352
  if ("ok" !== probeSimpleQuery) reasons.push("probe_simple_query_failed");
19340
19353
  return {
19341
- looksAutomated: reasons.length > 0,
19354
+ looksAutomated: automation.webdriver || automation.userAgentMatched,
19342
19355
  reasons: reasons.length > 0 ? reasons.join(",") : "none"
19343
19356
  };
19344
19357
  }
@@ -19355,7 +19368,7 @@ var __webpack_exports__ = {};
19355
19368
  const probeBase = probeWebSocketConstructor("wss://videomail.io");
19356
19369
  const probeWithSimpleQuery = probeWebSocketConstructor("wss://videomail.io/?a=b");
19357
19370
  const languages = getLanguagesValue();
19358
- const automationSignals = getAutomationSignals(probeBase, probeWithSimpleQuery);
19371
+ const automationSignals = getDiagnosticSignals(probeBase, probeWithSimpleQuery);
19359
19372
  const diagnosticPairs = [
19360
19373
  `webdriver=${String(navigator.webdriver)}`,
19361
19374
  `wsCtorNative=${String(wsCtorNative)}`,
@@ -19382,10 +19395,6 @@ var __webpack_exports__ = {};
19382
19395
  };
19383
19396
  }
19384
19397
  const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
19385
- function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19386
- return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19387
- }
19388
- const util_isAutomatedUserAgent = isAutomatedUserAgent;
19389
19398
  function boundedWriteQueue_define_property(obj, key, value) {
19390
19399
  if (key in obj) Object.defineProperty(obj, key, {
19391
19400
  value: value,
@@ -19581,6 +19590,9 @@ var __webpack_exports__ = {};
19581
19590
  explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
19582
19591
  options: this.options,
19583
19592
  cause: {
19593
+ transportPhase: "recording",
19594
+ mediaTransmissionStarted: true,
19595
+ automation: util_getAutomationSignals(),
19584
19596
  unconfirmedFrames,
19585
19597
  unconfirmedSamples
19586
19598
  }
@@ -19648,46 +19660,54 @@ var __webpack_exports__ = {};
19648
19660
  this.resumeAfterDrain = false;
19649
19661
  this.reconnecting = true;
19650
19662
  this.initSocket();
19651
- } else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
19652
- this.handleConnectionFailure({
19653
- url2Connect,
19654
- cause: "closed"
19655
- });
19656
- }, 0);
19663
+ } else if (this.shouldReportStreamClose()) {
19664
+ const stream = this.stream;
19665
+ window.setTimeout(()=>{
19666
+ if (this.stream === stream) this.handleConnectionFailure({
19667
+ url2Connect,
19668
+ cause: "closed"
19669
+ });
19670
+ }, 0);
19671
+ }
19657
19672
  }
19658
19673
  shouldIgnoreStreamClose() {
19659
- return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
19674
+ return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
19675
+ }
19676
+ closeIdleConnection(reason) {
19677
+ if (this.mediaTransmissionStarted) return;
19678
+ this.options.logger.debug(string_summarize("Recorder: closing idle connection", {
19679
+ reason,
19680
+ connected: this.connected,
19681
+ connecting: this.connecting,
19682
+ visibilityState: document.visibilityState
19683
+ }));
19684
+ this.connected = this.connecting = this.reconnecting = false;
19685
+ this.clearConnectionTimeout();
19686
+ this.clearReconnectTimeout();
19687
+ this.stopPings();
19688
+ this.discardSocket();
19660
19689
  }
19661
19690
  handleStreamError(url2Connect, err) {
19662
- var _this_lastSocketError;
19663
19691
  if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
19664
19692
  else this.lastSocketError = serializeError(err);
19665
19693
  const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
19666
19694
  error: serializeError(err)
19667
19695
  });
19668
19696
  this.options.logger.debug(debugLine);
19669
- if (this.validate()) {
19697
+ if (this.shouldIgnoreStreamClose()) {
19670
19698
  this.connected = false;
19671
19699
  this.stopPings();
19672
19700
  this.discardSocket();
19673
19701
  return;
19674
19702
  }
19675
- if (!this.connected) return void this.handleConnectionFailure({
19703
+ if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19704
+ this.reconnecting || (this.reconnecting = this.canReconnectAfterStreamClose());
19705
+ this.connected = false;
19706
+ this.stopPings();
19707
+ this.handleConnectionFailure({
19676
19708
  url2Connect,
19677
19709
  cause: "error"
19678
19710
  });
19679
- const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
19680
- cause: null != (_this_lastSocketError = this.lastSocketError) ? _this_lastSocketError : err
19681
- });
19682
- if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19683
- this.emit("ERROR", {
19684
- err: error_createError({
19685
- message: "WebSocket stream error",
19686
- explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
19687
- options: this.options,
19688
- exc: streamError
19689
- })
19690
- });
19691
19711
  }
19692
19712
  sendPings() {
19693
19713
  this.stopPings();
@@ -19748,6 +19768,7 @@ var __webpack_exports__ = {};
19748
19768
  this.connectionFailed = true;
19749
19769
  this.connecting = false;
19750
19770
  this.reconnecting = false;
19771
+ const reconnectAttempts = this.reconnectAttempts;
19751
19772
  this.reconnectAttempts = 0;
19752
19773
  this.clearReconnectTimeout();
19753
19774
  this.clearConnectionTimeout();
@@ -19758,6 +19779,14 @@ var __webpack_exports__ = {};
19758
19779
  const socketError = this.lastSocketError;
19759
19780
  const contents = {
19760
19781
  cause,
19782
+ transportPhase: void 0 === this.connectionOpenedAt ? "initial" : "idle",
19783
+ mediaTransmissionStarted: this.mediaTransmissionStarted,
19784
+ recordingActive: this.recordingActive,
19785
+ framesCount: this.framesCount,
19786
+ samplesCount: this.samplesCount,
19787
+ reconnectAttempts,
19788
+ connectionOpenedAt: this.connectionOpenedAt,
19789
+ automation: util_getAutomationSignals(),
19761
19790
  closeCode: null == closeEvent ? void 0 : closeEvent.code,
19762
19791
  closeReason: null == closeEvent ? void 0 : closeEvent.reason,
19763
19792
  elapsedMs,
@@ -19799,7 +19828,7 @@ var __webpack_exports__ = {};
19799
19828
  });
19800
19829
  }
19801
19830
  shouldIgnoreConnectionFailure() {
19802
- return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
19831
+ return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
19803
19832
  }
19804
19833
  initSocket(cb) {
19805
19834
  if (this.connected) {
@@ -19809,7 +19838,8 @@ var __webpack_exports__ = {};
19809
19838
  }
19810
19839
  if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
19811
19840
  this.clearReconnectTimeout();
19812
- if (util_isAutomatedUserAgent()) {
19841
+ const automation = util_getAutomationSignals();
19842
+ if (automation.userAgentMatched) {
19813
19843
  this.connecting = false;
19814
19844
  this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
19815
19845
  return;
@@ -19843,6 +19873,8 @@ var __webpack_exports__ = {};
19843
19873
  try {
19844
19874
  nativeSocket = new WebSocket(url2Connect);
19845
19875
  nativeSocket.addEventListener("close", (event)=>{
19876
+ var _this_stream;
19877
+ if ((null == (_this_stream = this.stream) ? void 0 : _this_stream.socket) !== nativeSocket) return;
19846
19878
  this.lastCloseEvent = {
19847
19879
  code: event.code,
19848
19880
  reason: event.reason,
@@ -19850,6 +19882,8 @@ var __webpack_exports__ = {};
19850
19882
  };
19851
19883
  });
19852
19884
  nativeSocket.addEventListener("error", (event)=>{
19885
+ var _this_stream;
19886
+ if ((null == (_this_stream = this.stream) ? void 0 : _this_stream.socket) !== nativeSocket) return;
19853
19887
  this.lastSocketError = error_getEventDetails(event);
19854
19888
  });
19855
19889
  } catch (exc) {
@@ -19861,7 +19895,10 @@ var __webpack_exports__ = {};
19861
19895
  message,
19862
19896
  explanation,
19863
19897
  options: this.options,
19864
- exc
19898
+ exc,
19899
+ cause: {
19900
+ automation
19901
+ }
19865
19902
  });
19866
19903
  this.emit("ERROR", {
19867
19904
  err
@@ -19899,15 +19936,16 @@ var __webpack_exports__ = {};
19899
19936
  });
19900
19937
  }, connectionTimeoutMs);
19901
19938
  stream.on("close", ()=>{
19902
- this.handleStreamClose(url2Connect);
19939
+ if (this.stream === stream) this.handleStreamClose(url2Connect);
19903
19940
  });
19904
19941
  stream.on("connect", (args)=>{
19905
- var _this_stream;
19942
+ if (this.stream !== stream) return;
19906
19943
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
19907
19944
  this.clearConnectionTimeout();
19908
- const isClosing = (null == (_this_stream = this.stream) ? void 0 : _this_stream.socket.readyState) === WebSocket.CLOSING;
19945
+ const isClosing = stream.socket.readyState === WebSocket.CLOSING;
19909
19946
  if (!this.connected && !isClosing && !this.unloaded) {
19910
19947
  this.connected = true;
19948
+ this.connectionOpenedAt = Date.now();
19911
19949
  this.connecting = this.unloaded = false;
19912
19950
  this.reconnecting = false;
19913
19951
  this.reconnectAttempts = 0;
@@ -19917,6 +19955,7 @@ var __webpack_exports__ = {};
19917
19955
  }
19918
19956
  });
19919
19957
  stream.on("data", (data)=>{
19958
+ if (this.stream !== stream) return;
19920
19959
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
19921
19960
  try {
19922
19961
  const command = JSON.parse(data.toString());
@@ -19935,10 +19974,10 @@ var __webpack_exports__ = {};
19935
19974
  }
19936
19975
  });
19937
19976
  stream.on("error", (err)=>{
19938
- this.handleStreamError(url2Connect, err);
19977
+ if (this.stream === stream) this.handleStreamError(url2Connect, err);
19939
19978
  });
19940
19979
  stream.on("drain", ()=>{
19941
- this.handleStreamDrain(stream);
19980
+ if (this.stream === stream) this.handleStreamDrain(stream);
19942
19981
  });
19943
19982
  stream.on("preend", (args)=>{
19944
19983
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
@@ -19992,7 +20031,7 @@ var __webpack_exports__ = {};
19992
20031
  }
19993
20032
  }
19994
20033
  constructor(options){
19995
- super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0);
20034
+ super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0), connection_define_property(this, "connectionOpenedAt", void 0);
19996
20035
  }
19997
20036
  }
19998
20037
  const connection = RecorderConnection;
@@ -20055,6 +20094,7 @@ var __webpack_exports__ = {};
20055
20094
  const characteristics = null == (_this_userMedia = this.userMedia) ? void 0 : _this_userMedia.getCharacteristics();
20056
20095
  this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
20057
20096
  const errorListeners = this.getListeners("ERROR");
20097
+ if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) this.closeIdleConnection("camera-access-failed");
20058
20098
  if (null == errorListeners ? void 0 : errorListeners.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
20059
20099
  const videomailError = error_createError({
20060
20100
  err,
@@ -21832,6 +21872,7 @@ var __webpack_exports__ = {};
21832
21872
  this.cancelCameraRecovery();
21833
21873
  this.blocking = true;
21834
21874
  this.clearUserMediaTimeout();
21875
+ this.closeIdleConnection("blocking-error");
21835
21876
  });
21836
21877
  this.on("PREVIEW", ()=>{
21837
21878
  this.hide();
package/dist/esm/index.js CHANGED
@@ -3620,7 +3620,7 @@ const constants = {
3620
3620
  }
3621
3621
  };
3622
3622
  var package_namespaceObject = {
3623
- rE: "16.6.0"
3623
+ rE: "16.7.0"
3624
3624
  };
3625
3625
  function isAudioEnabled(options) {
3626
3626
  return Boolean(options.audio.enabled);
@@ -6119,6 +6119,18 @@ function summarize(title, contents) {
6119
6119
  return line;
6120
6120
  }
6121
6121
  const string_summarize = summarize;
6122
+ function isAutomatedUserAgent(userAgent = navigator.userAgent) {
6123
+ return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
6124
+ }
6125
+ const util_isAutomatedUserAgent = isAutomatedUserAgent;
6126
+ function getAutomationSignals() {
6127
+ const webdriver = navigator.webdriver;
6128
+ return {
6129
+ webdriver: true === webdriver,
6130
+ userAgentMatched: util_isAutomatedUserAgent()
6131
+ };
6132
+ }
6133
+ const util_getAutomationSignals = getAutomationSignals;
6122
6134
  function getEdgeCodes(value, amount, fromEnd) {
6123
6135
  const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
6124
6136
  const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
@@ -6146,15 +6158,16 @@ function getLanguagesValue() {
6146
6158
  if (languageList.length > 0) return languageList.join(",");
6147
6159
  return navigator.language || "none";
6148
6160
  }
6149
- function getAutomationSignals(probeBase, probeSimpleQuery) {
6161
+ function getDiagnosticSignals(probeBase, probeSimpleQuery) {
6150
6162
  const reasons = [];
6151
- if (navigator.webdriver) reasons.push("webdriver=true");
6152
- if (/HeadlessChrome|PhantomJS|puppeteer|playwright/iu.test(navigator.userAgent)) reasons.push("headless_ua_marker");
6163
+ const automation = util_getAutomationSignals();
6164
+ if (automation.webdriver) reasons.push("webdriver=true");
6165
+ if (automation.userAgentMatched) reasons.push("headless_ua_marker");
6153
6166
  if (0 === navigator.languages.length) reasons.push("no_languages");
6154
6167
  if ("ok" !== probeBase) reasons.push("probe_base_failed");
6155
6168
  if ("ok" !== probeSimpleQuery) reasons.push("probe_simple_query_failed");
6156
6169
  return {
6157
- looksAutomated: reasons.length > 0,
6170
+ looksAutomated: automation.webdriver || automation.userAgentMatched,
6158
6171
  reasons: reasons.length > 0 ? reasons.join(",") : "none"
6159
6172
  };
6160
6173
  }
@@ -6171,7 +6184,7 @@ function getWebSocketDiagnostic(url2Connect) {
6171
6184
  const probeBase = probeWebSocketConstructor("wss://videomail.io");
6172
6185
  const probeWithSimpleQuery = probeWebSocketConstructor("wss://videomail.io/?a=b");
6173
6186
  const languages = getLanguagesValue();
6174
- const automationSignals = getAutomationSignals(probeBase, probeWithSimpleQuery);
6187
+ const automationSignals = getDiagnosticSignals(probeBase, probeWithSimpleQuery);
6175
6188
  const diagnosticPairs = [
6176
6189
  `webdriver=${String(navigator.webdriver)}`,
6177
6190
  `wsCtorNative=${String(wsCtorNative)}`,
@@ -6198,10 +6211,6 @@ function getWebSocketDiagnostic(url2Connect) {
6198
6211
  };
6199
6212
  }
6200
6213
  const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
6201
- function isAutomatedUserAgent(userAgent = navigator.userAgent) {
6202
- return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
6203
- }
6204
- const util_isAutomatedUserAgent = isAutomatedUserAgent;
6205
6214
  function boundedWriteQueue_define_property(obj, key, value) {
6206
6215
  if (key in obj) Object.defineProperty(obj, key, {
6207
6216
  value: value,
@@ -6397,6 +6406,9 @@ class RecorderStreamWriter extends util_Despot {
6397
6406
  explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
6398
6407
  options: this.options,
6399
6408
  cause: {
6409
+ transportPhase: "recording",
6410
+ mediaTransmissionStarted: true,
6411
+ automation: util_getAutomationSignals(),
6400
6412
  unconfirmedFrames,
6401
6413
  unconfirmedSamples
6402
6414
  }
@@ -6464,15 +6476,32 @@ class RecorderConnection extends streamWriter {
6464
6476
  this.resumeAfterDrain = false;
6465
6477
  this.reconnecting = true;
6466
6478
  this.initSocket();
6467
- } else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
6468
- this.handleConnectionFailure({
6469
- url2Connect,
6470
- cause: "closed"
6471
- });
6472
- }, 0);
6479
+ } else if (this.shouldReportStreamClose()) {
6480
+ const stream = this.stream;
6481
+ window.setTimeout(()=>{
6482
+ if (this.stream === stream) this.handleConnectionFailure({
6483
+ url2Connect,
6484
+ cause: "closed"
6485
+ });
6486
+ }, 0);
6487
+ }
6473
6488
  }
6474
6489
  shouldIgnoreStreamClose() {
6475
- return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
6490
+ return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
6491
+ }
6492
+ closeIdleConnection(reason) {
6493
+ if (this.mediaTransmissionStarted) return;
6494
+ this.options.logger.debug(string_summarize("Recorder: closing idle connection", {
6495
+ reason,
6496
+ connected: this.connected,
6497
+ connecting: this.connecting,
6498
+ visibilityState: document.visibilityState
6499
+ }));
6500
+ this.connected = this.connecting = this.reconnecting = false;
6501
+ this.clearConnectionTimeout();
6502
+ this.clearReconnectTimeout();
6503
+ this.stopPings();
6504
+ this.discardSocket();
6476
6505
  }
6477
6506
  handleStreamError(url2Connect, err) {
6478
6507
  if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
@@ -6481,28 +6510,20 @@ class RecorderConnection extends streamWriter {
6481
6510
  error: serializeError(err)
6482
6511
  });
6483
6512
  this.options.logger.debug(debugLine);
6484
- if (this.validate()) {
6513
+ if (this.shouldIgnoreStreamClose()) {
6485
6514
  this.connected = false;
6486
6515
  this.stopPings();
6487
6516
  this.discardSocket();
6488
6517
  return;
6489
6518
  }
6490
- if (!this.connected) return void this.handleConnectionFailure({
6519
+ if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
6520
+ this.reconnecting ||= this.canReconnectAfterStreamClose();
6521
+ this.connected = false;
6522
+ this.stopPings();
6523
+ this.handleConnectionFailure({
6491
6524
  url2Connect,
6492
6525
  cause: "error"
6493
6526
  });
6494
- const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
6495
- cause: this.lastSocketError ?? err
6496
- });
6497
- if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
6498
- this.emit("ERROR", {
6499
- err: error_createError({
6500
- message: "WebSocket stream error",
6501
- explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
6502
- options: this.options,
6503
- exc: streamError
6504
- })
6505
- });
6506
6527
  }
6507
6528
  sendPings() {
6508
6529
  this.stopPings();
@@ -6562,6 +6583,7 @@ class RecorderConnection extends streamWriter {
6562
6583
  this.connectionFailed = true;
6563
6584
  this.connecting = false;
6564
6585
  this.reconnecting = false;
6586
+ const reconnectAttempts = this.reconnectAttempts;
6565
6587
  this.reconnectAttempts = 0;
6566
6588
  this.clearReconnectTimeout();
6567
6589
  this.clearConnectionTimeout();
@@ -6572,6 +6594,14 @@ class RecorderConnection extends streamWriter {
6572
6594
  const socketError = this.lastSocketError;
6573
6595
  const contents = {
6574
6596
  cause,
6597
+ transportPhase: void 0 === this.connectionOpenedAt ? "initial" : "idle",
6598
+ mediaTransmissionStarted: this.mediaTransmissionStarted,
6599
+ recordingActive: this.recordingActive,
6600
+ framesCount: this.framesCount,
6601
+ samplesCount: this.samplesCount,
6602
+ reconnectAttempts,
6603
+ connectionOpenedAt: this.connectionOpenedAt,
6604
+ automation: util_getAutomationSignals(),
6575
6605
  closeCode: closeEvent?.code,
6576
6606
  closeReason: closeEvent?.reason,
6577
6607
  elapsedMs,
@@ -6613,7 +6643,7 @@ class RecorderConnection extends streamWriter {
6613
6643
  });
6614
6644
  }
6615
6645
  shouldIgnoreConnectionFailure() {
6616
- return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
6646
+ return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
6617
6647
  }
6618
6648
  initSocket(cb) {
6619
6649
  if (this.connected) {
@@ -6623,7 +6653,8 @@ class RecorderConnection extends streamWriter {
6623
6653
  }
6624
6654
  if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
6625
6655
  this.clearReconnectTimeout();
6626
- if (util_isAutomatedUserAgent()) {
6656
+ const automation = util_getAutomationSignals();
6657
+ if (automation.userAgentMatched) {
6627
6658
  this.connecting = false;
6628
6659
  this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
6629
6660
  return;
@@ -6657,6 +6688,7 @@ class RecorderConnection extends streamWriter {
6657
6688
  try {
6658
6689
  nativeSocket = new WebSocket(url2Connect);
6659
6690
  nativeSocket.addEventListener("close", (event)=>{
6691
+ if (this.stream?.socket !== nativeSocket) return;
6660
6692
  this.lastCloseEvent = {
6661
6693
  code: event.code,
6662
6694
  reason: event.reason,
@@ -6664,6 +6696,7 @@ class RecorderConnection extends streamWriter {
6664
6696
  };
6665
6697
  });
6666
6698
  nativeSocket.addEventListener("error", (event)=>{
6699
+ if (this.stream?.socket !== nativeSocket) return;
6667
6700
  this.lastSocketError = error_getEventDetails(event);
6668
6701
  });
6669
6702
  } catch (exc) {
@@ -6675,7 +6708,10 @@ class RecorderConnection extends streamWriter {
6675
6708
  message,
6676
6709
  explanation,
6677
6710
  options: this.options,
6678
- exc
6711
+ exc,
6712
+ cause: {
6713
+ automation
6714
+ }
6679
6715
  });
6680
6716
  this.emit("ERROR", {
6681
6717
  err
@@ -6713,14 +6749,16 @@ class RecorderConnection extends streamWriter {
6713
6749
  });
6714
6750
  }, connectionTimeoutMs);
6715
6751
  stream.on("close", ()=>{
6716
- this.handleStreamClose(url2Connect);
6752
+ if (this.stream === stream) this.handleStreamClose(url2Connect);
6717
6753
  });
6718
6754
  stream.on("connect", (args)=>{
6755
+ if (this.stream !== stream) return;
6719
6756
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
6720
6757
  this.clearConnectionTimeout();
6721
- const isClosing = this.stream?.socket.readyState === WebSocket.CLOSING;
6758
+ const isClosing = stream.socket.readyState === WebSocket.CLOSING;
6722
6759
  if (!this.connected && !isClosing && !this.unloaded) {
6723
6760
  this.connected = true;
6761
+ this.connectionOpenedAt = Date.now();
6724
6762
  this.connecting = this.unloaded = false;
6725
6763
  this.reconnecting = false;
6726
6764
  this.reconnectAttempts = 0;
@@ -6730,6 +6768,7 @@ class RecorderConnection extends streamWriter {
6730
6768
  }
6731
6769
  });
6732
6770
  stream.on("data", (data)=>{
6771
+ if (this.stream !== stream) return;
6733
6772
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
6734
6773
  try {
6735
6774
  const command = JSON.parse(data.toString());
@@ -6748,10 +6787,10 @@ class RecorderConnection extends streamWriter {
6748
6787
  }
6749
6788
  });
6750
6789
  stream.on("error", (err)=>{
6751
- this.handleStreamError(url2Connect, err);
6790
+ if (this.stream === stream) this.handleStreamError(url2Connect, err);
6752
6791
  });
6753
6792
  stream.on("drain", ()=>{
6754
- this.handleStreamDrain(stream);
6793
+ if (this.stream === stream) this.handleStreamDrain(stream);
6755
6794
  });
6756
6795
  stream.on("preend", (args)=>{
6757
6796
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
@@ -6805,7 +6844,7 @@ class RecorderConnection extends streamWriter {
6805
6844
  }
6806
6845
  }
6807
6846
  constructor(options){
6808
- super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0);
6847
+ super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0), connection_define_property(this, "connectionOpenedAt", void 0);
6809
6848
  }
6810
6849
  }
6811
6850
  const connection = RecorderConnection;
@@ -6867,6 +6906,7 @@ class RecorderMedia extends connection {
6867
6906
  const characteristics = this.userMedia?.getCharacteristics();
6868
6907
  this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
6869
6908
  const errorListeners = this.getListeners("ERROR");
6909
+ if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) this.closeIdleConnection("camera-access-failed");
6870
6910
  if (errorListeners?.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
6871
6911
  const videomailError = error_createError({
6872
6912
  err,
@@ -8490,6 +8530,7 @@ class Recorder extends capture {
8490
8530
  this.cancelCameraRecovery();
8491
8531
  this.blocking = true;
8492
8532
  this.clearUserMediaTimeout();
8533
+ this.closeIdleConnection("blocking-error");
8493
8534
  });
8494
8535
  this.on("PREVIEW", ()=>{
8495
8536
  this.hide();
@@ -0,0 +1,5 @@
1
+ declare function getAutomationSignals(): {
2
+ webdriver: boolean;
3
+ userAgentMatched: boolean;
4
+ };
5
+ export default getAutomationSignals;
@@ -20,6 +20,7 @@ declare abstract class RecorderConnection extends RecorderStreamWriter {
20
20
  } | undefined;
21
21
  protected lastSocketError?: Record<string, unknown> | undefined;
22
22
  protected pingInterval?: number | undefined;
23
+ private connectionOpenedAt?;
23
24
  protected abstract executeCommand(command: VideomailCommand): void;
24
25
  abstract isUserMediaLoaded(): boolean | undefined;
25
26
  abstract validate(): boolean;
@@ -28,6 +29,7 @@ declare abstract class RecorderConnection extends RecorderStreamWriter {
28
29
  private shouldReportStreamClose;
29
30
  private handleStreamClose;
30
31
  private shouldIgnoreStreamClose;
32
+ protected closeIdleConnection(reason: string): void;
31
33
  private handleStreamError;
32
34
  protected sendPings(): void;
33
35
  protected stopPings(): void;
package/dist/umd/index.js CHANGED
@@ -7218,7 +7218,7 @@
7218
7218
  const qs = __webpack_require__("b");
7219
7219
  const RequestBase = __webpack_require__("d");
7220
7220
  const { isObject, mixin, hasOwn, isSafeKey } = __webpack_require__("Dp");
7221
- const ResponseBase = __webpack_require__("es");
7221
+ const ResponseBase = __webpack_require__("e");
7222
7222
  const Agent = __webpack_require__("pf");
7223
7223
  function noop() {}
7224
7224
  module1.exports = function(method, url) {
@@ -7932,7 +7932,7 @@
7932
7932
  }, this._responseTimeout);
7933
7933
  };
7934
7934
  },
7935
- es (module1, __unused_rspack_exports, __webpack_require__) {
7935
+ e (module1, __unused_rspack_exports, __webpack_require__) {
7936
7936
  "use strict";
7937
7937
  const utils = __webpack_require__("Dp");
7938
7938
  module1.exports = ResponseBase;
@@ -10643,7 +10643,7 @@
10643
10643
  return i[1];
10644
10644
  };
10645
10645
  },
10646
- R (module1) {
10646
+ RR (module1) {
10647
10647
  "use strict";
10648
10648
  var stylesInDOM = [];
10649
10649
  function getIndexByIdentifier(identifier) {
@@ -11144,7 +11144,7 @@
11144
11144
  var client = __webpack_require__("Q");
11145
11145
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11146
11146
  var package_namespaceObject = {
11147
- rE: "16.6.0"
11147
+ rE: "16.7.0"
11148
11148
  };
11149
11149
  function isAudioEnabled(options) {
11150
11150
  return Boolean(options.audio.enabled);
@@ -15724,7 +15724,7 @@
15724
15724
  return newOptions;
15725
15725
  }
15726
15726
  const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
15727
- var injectStylesIntoStyleTag = __webpack_require__("R");
15727
+ var injectStylesIntoStyleTag = __webpack_require__("RR");
15728
15728
  var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
15729
15729
  var styleDomAPI = __webpack_require__("Bd");
15730
15730
  var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
@@ -19070,6 +19070,18 @@
19070
19070
  canvas_to_buffer_modern_r.atob = void 0;
19071
19071
  var websocket_stream_stream = __webpack_require__("U");
19072
19072
  var stream_default = /*#__PURE__*/ __webpack_require__.n(websocket_stream_stream);
19073
+ function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19074
+ return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19075
+ }
19076
+ const util_isAutomatedUserAgent = isAutomatedUserAgent;
19077
+ function getAutomationSignals() {
19078
+ const webdriver = navigator.webdriver;
19079
+ return {
19080
+ webdriver: true === webdriver,
19081
+ userAgentMatched: util_isAutomatedUserAgent()
19082
+ };
19083
+ }
19084
+ const util_getAutomationSignals = getAutomationSignals;
19073
19085
  function getEdgeCodes(value, amount, fromEnd) {
19074
19086
  const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
19075
19087
  const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
@@ -19097,15 +19109,16 @@
19097
19109
  if (languageList.length > 0) return languageList.join(",");
19098
19110
  return navigator.language || "none";
19099
19111
  }
19100
- function getAutomationSignals(probeBase, probeSimpleQuery) {
19112
+ function getDiagnosticSignals(probeBase, probeSimpleQuery) {
19101
19113
  const reasons = [];
19102
- if (navigator.webdriver) reasons.push("webdriver=true");
19103
- if (/HeadlessChrome|PhantomJS|puppeteer|playwright/iu.test(navigator.userAgent)) reasons.push("headless_ua_marker");
19114
+ const automation = util_getAutomationSignals();
19115
+ if (automation.webdriver) reasons.push("webdriver=true");
19116
+ if (automation.userAgentMatched) reasons.push("headless_ua_marker");
19104
19117
  if (0 === navigator.languages.length) reasons.push("no_languages");
19105
19118
  if ("ok" !== probeBase) reasons.push("probe_base_failed");
19106
19119
  if ("ok" !== probeSimpleQuery) reasons.push("probe_simple_query_failed");
19107
19120
  return {
19108
- looksAutomated: reasons.length > 0,
19121
+ looksAutomated: automation.webdriver || automation.userAgentMatched,
19109
19122
  reasons: reasons.length > 0 ? reasons.join(",") : "none"
19110
19123
  };
19111
19124
  }
@@ -19122,7 +19135,7 @@
19122
19135
  const probeBase = probeWebSocketConstructor("wss://videomail.io");
19123
19136
  const probeWithSimpleQuery = probeWebSocketConstructor("wss://videomail.io/?a=b");
19124
19137
  const languages = getLanguagesValue();
19125
- const automationSignals = getAutomationSignals(probeBase, probeWithSimpleQuery);
19138
+ const automationSignals = getDiagnosticSignals(probeBase, probeWithSimpleQuery);
19126
19139
  const diagnosticPairs = [
19127
19140
  `webdriver=${String(navigator.webdriver)}`,
19128
19141
  `wsCtorNative=${String(wsCtorNative)}`,
@@ -19149,10 +19162,6 @@
19149
19162
  };
19150
19163
  }
19151
19164
  const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
19152
- function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19153
- return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19154
- }
19155
- const util_isAutomatedUserAgent = isAutomatedUserAgent;
19156
19165
  const DEFAULT_MAX_QUEUED_BYTES = 1048576;
19157
19166
  class BoundedWriteQueue {
19158
19167
  maxBytes;
@@ -19346,6 +19355,9 @@
19346
19355
  explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
19347
19356
  options: this.options,
19348
19357
  cause: {
19358
+ transportPhase: "recording",
19359
+ mediaTransmissionStarted: true,
19360
+ automation: util_getAutomationSignals(),
19349
19361
  unconfirmedFrames,
19350
19362
  unconfirmedSamples
19351
19363
  }
@@ -19387,6 +19399,7 @@
19387
19399
  lastCloseEvent;
19388
19400
  lastSocketError;
19389
19401
  pingInterval;
19402
+ connectionOpenedAt;
19390
19403
  constructor(options){
19391
19404
  super(options);
19392
19405
  }
@@ -19415,15 +19428,32 @@
19415
19428
  this.resumeAfterDrain = false;
19416
19429
  this.reconnecting = true;
19417
19430
  this.initSocket();
19418
- } else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
19419
- this.handleConnectionFailure({
19420
- url2Connect,
19421
- cause: "closed"
19422
- });
19423
- }, 0);
19431
+ } else if (this.shouldReportStreamClose()) {
19432
+ const stream = this.stream;
19433
+ window.setTimeout(()=>{
19434
+ if (this.stream === stream) this.handleConnectionFailure({
19435
+ url2Connect,
19436
+ cause: "closed"
19437
+ });
19438
+ }, 0);
19439
+ }
19424
19440
  }
19425
19441
  shouldIgnoreStreamClose() {
19426
- return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
19442
+ return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
19443
+ }
19444
+ closeIdleConnection(reason) {
19445
+ if (this.mediaTransmissionStarted) return;
19446
+ this.options.logger.debug(string_summarize("Recorder: closing idle connection", {
19447
+ reason,
19448
+ connected: this.connected,
19449
+ connecting: this.connecting,
19450
+ visibilityState: document.visibilityState
19451
+ }));
19452
+ this.connected = this.connecting = this.reconnecting = false;
19453
+ this.clearConnectionTimeout();
19454
+ this.clearReconnectTimeout();
19455
+ this.stopPings();
19456
+ this.discardSocket();
19427
19457
  }
19428
19458
  handleStreamError(url2Connect, err) {
19429
19459
  if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
@@ -19432,28 +19462,20 @@
19432
19462
  error: serializeError(err)
19433
19463
  });
19434
19464
  this.options.logger.debug(debugLine);
19435
- if (this.validate()) {
19465
+ if (this.shouldIgnoreStreamClose()) {
19436
19466
  this.connected = false;
19437
19467
  this.stopPings();
19438
19468
  this.discardSocket();
19439
19469
  return;
19440
19470
  }
19441
- if (!this.connected) return void this.handleConnectionFailure({
19471
+ if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19472
+ this.reconnecting ||= this.canReconnectAfterStreamClose();
19473
+ this.connected = false;
19474
+ this.stopPings();
19475
+ this.handleConnectionFailure({
19442
19476
  url2Connect,
19443
19477
  cause: "error"
19444
19478
  });
19445
- const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
19446
- cause: this.lastSocketError ?? err
19447
- });
19448
- if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19449
- this.emit("ERROR", {
19450
- err: error_createError({
19451
- message: "WebSocket stream error",
19452
- explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
19453
- options: this.options,
19454
- exc: streamError
19455
- })
19456
- });
19457
19479
  }
19458
19480
  sendPings() {
19459
19481
  this.stopPings();
@@ -19513,6 +19535,7 @@
19513
19535
  this.connectionFailed = true;
19514
19536
  this.connecting = false;
19515
19537
  this.reconnecting = false;
19538
+ const reconnectAttempts = this.reconnectAttempts;
19516
19539
  this.reconnectAttempts = 0;
19517
19540
  this.clearReconnectTimeout();
19518
19541
  this.clearConnectionTimeout();
@@ -19523,6 +19546,14 @@
19523
19546
  const socketError = this.lastSocketError;
19524
19547
  const contents = {
19525
19548
  cause,
19549
+ transportPhase: void 0 === this.connectionOpenedAt ? "initial" : "idle",
19550
+ mediaTransmissionStarted: this.mediaTransmissionStarted,
19551
+ recordingActive: this.recordingActive,
19552
+ framesCount: this.framesCount,
19553
+ samplesCount: this.samplesCount,
19554
+ reconnectAttempts,
19555
+ connectionOpenedAt: this.connectionOpenedAt,
19556
+ automation: util_getAutomationSignals(),
19526
19557
  closeCode: closeEvent?.code,
19527
19558
  closeReason: closeEvent?.reason,
19528
19559
  elapsedMs,
@@ -19564,7 +19595,7 @@
19564
19595
  });
19565
19596
  }
19566
19597
  shouldIgnoreConnectionFailure() {
19567
- return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
19598
+ return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
19568
19599
  }
19569
19600
  initSocket(cb) {
19570
19601
  if (this.connected) {
@@ -19574,7 +19605,8 @@
19574
19605
  }
19575
19606
  if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
19576
19607
  this.clearReconnectTimeout();
19577
- if (util_isAutomatedUserAgent()) {
19608
+ const automation = util_getAutomationSignals();
19609
+ if (automation.userAgentMatched) {
19578
19610
  this.connecting = false;
19579
19611
  this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
19580
19612
  return;
@@ -19608,6 +19640,7 @@
19608
19640
  try {
19609
19641
  nativeSocket = new WebSocket(url2Connect);
19610
19642
  nativeSocket.addEventListener("close", (event)=>{
19643
+ if (this.stream?.socket !== nativeSocket) return;
19611
19644
  this.lastCloseEvent = {
19612
19645
  code: event.code,
19613
19646
  reason: event.reason,
@@ -19615,6 +19648,7 @@
19615
19648
  };
19616
19649
  });
19617
19650
  nativeSocket.addEventListener("error", (event)=>{
19651
+ if (this.stream?.socket !== nativeSocket) return;
19618
19652
  this.lastSocketError = error_getEventDetails(event);
19619
19653
  });
19620
19654
  } catch (exc) {
@@ -19626,7 +19660,10 @@
19626
19660
  message,
19627
19661
  explanation,
19628
19662
  options: this.options,
19629
- exc
19663
+ exc,
19664
+ cause: {
19665
+ automation
19666
+ }
19630
19667
  });
19631
19668
  this.emit("ERROR", {
19632
19669
  err
@@ -19664,14 +19701,16 @@
19664
19701
  });
19665
19702
  }, connectionTimeoutMs);
19666
19703
  stream.on("close", ()=>{
19667
- this.handleStreamClose(url2Connect);
19704
+ if (this.stream === stream) this.handleStreamClose(url2Connect);
19668
19705
  });
19669
19706
  stream.on("connect", (args)=>{
19707
+ if (this.stream !== stream) return;
19670
19708
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
19671
19709
  this.clearConnectionTimeout();
19672
- const isClosing = this.stream?.socket.readyState === WebSocket.CLOSING;
19710
+ const isClosing = stream.socket.readyState === WebSocket.CLOSING;
19673
19711
  if (!this.connected && !isClosing && !this.unloaded) {
19674
19712
  this.connected = true;
19713
+ this.connectionOpenedAt = Date.now();
19675
19714
  this.connecting = this.unloaded = false;
19676
19715
  this.reconnecting = false;
19677
19716
  this.reconnectAttempts = 0;
@@ -19681,6 +19720,7 @@
19681
19720
  }
19682
19721
  });
19683
19722
  stream.on("data", (data)=>{
19723
+ if (this.stream !== stream) return;
19684
19724
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
19685
19725
  try {
19686
19726
  const command = JSON.parse(data.toString());
@@ -19699,10 +19739,10 @@
19699
19739
  }
19700
19740
  });
19701
19741
  stream.on("error", (err)=>{
19702
- this.handleStreamError(url2Connect, err);
19742
+ if (this.stream === stream) this.handleStreamError(url2Connect, err);
19703
19743
  });
19704
19744
  stream.on("drain", ()=>{
19705
- this.handleStreamDrain(stream);
19745
+ if (this.stream === stream) this.handleStreamDrain(stream);
19706
19746
  });
19707
19747
  stream.on("preend", (args)=>{
19708
19748
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
@@ -19824,6 +19864,7 @@
19824
19864
  const characteristics = this.userMedia?.getCharacteristics();
19825
19865
  this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
19826
19866
  const errorListeners = this.getListeners("ERROR");
19867
+ if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) this.closeIdleConnection("camera-access-failed");
19827
19868
  if (errorListeners?.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
19828
19869
  const videomailError = error_createError({
19829
19870
  err,
@@ -21468,6 +21509,7 @@
21468
21509
  this.cancelCameraRecovery();
21469
21510
  this.blocking = true;
21470
21511
  this.clearUserMediaTimeout();
21512
+ this.closeIdleConnection("blocking-error");
21471
21513
  });
21472
21514
  this.on("PREVIEW", ()=>{
21473
21515
  this.hide();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "videomail-client",
3
- "version": "16.6.0",
3
+ "version": "16.7.0",
4
4
  "description": "A wicked npm package to record videos directly in the browser, for Deaf and Sign Language!",
5
5
  "keywords": [
6
6
  "webcam",
@@ -108,17 +108,17 @@
108
108
  "eslint-plugin-de-morgan": "2.3.0",
109
109
  "eslint-plugin-depend": "1.5.0",
110
110
  "eslint-plugin-import-x": "4.17.1",
111
- "eslint-plugin-package-json": "1.12.0",
112
- "eslint-plugin-regexp": "3.3.2",
111
+ "eslint-plugin-package-json": "1.13.0",
112
+ "eslint-plugin-regexp": "3.3.3",
113
113
  "eslint-plugin-simple-import-sort": "14.0.0",
114
- "globals": "17.13.0",
114
+ "globals": "17.14.0",
115
115
  "jsdom": "30.1.3",
116
116
  "msw": "3.0.3",
117
117
  "msw-storybook-addon": "3.0.3",
118
118
  "prettier": "3.9.10",
119
119
  "prettier-plugin-curly": "0.4.1",
120
120
  "prettier-plugin-jsdoc": "1.8.1",
121
- "prettier-plugin-packagejson": "3.0.3",
121
+ "prettier-plugin-packagejson": "3.1.0",
122
122
  "prettier-plugin-sh": "0.20.2",
123
123
  "release-it": "21.1.2",
124
124
  "storybook": "10.6.5",