@eyepop.ai/eyepop 3.19.0 → 3.19.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.
@@ -11432,57 +11432,8 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
11432
11432
  };
11433
11433
  }
11434
11434
 
11435
- // ../../node_modules/url-join/lib/url-join.js
11436
- function normalize(strArray) {
11437
- var resultArray = [];
11438
- if (strArray.length === 0) {
11439
- return "";
11440
- }
11441
- if (typeof strArray[0] !== "string") {
11442
- throw new TypeError("Url must be a string. Received " + strArray[0]);
11443
- }
11444
- if (strArray[0].match(/^[^/:]+:\/*$/) && strArray.length > 1) {
11445
- var first = strArray.shift();
11446
- strArray[0] = first + strArray[0];
11447
- }
11448
- if (strArray[0].match(/^file:\/\/\//)) {
11449
- strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1:///");
11450
- } else {
11451
- strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1://");
11452
- }
11453
- for (var i = 0; i < strArray.length; i++) {
11454
- var component = strArray[i];
11455
- if (typeof component !== "string") {
11456
- throw new TypeError("Url must be a string. Received " + component);
11457
- }
11458
- if (component === "") {
11459
- continue;
11460
- }
11461
- if (i > 0) {
11462
- component = component.replace(/^[\/]+/, "");
11463
- }
11464
- if (i < strArray.length - 1) {
11465
- component = component.replace(/[\/]+$/, "");
11466
- } else {
11467
- component = component.replace(/[\/]+$/, "/");
11468
- }
11469
- resultArray.push(component);
11470
- }
11471
- var str = resultArray.join("/");
11472
- str = str.replace(/\/(\?|&|#[^!])/g, "$1");
11473
- var parts = str.split("?");
11474
- str = parts.shift() + (parts.length > 0 ? "?" : "") + parts.join("&");
11475
- return str;
11476
- }
11477
- function urlJoin() {
11478
- var input;
11479
- if (typeof arguments[0] === "object") {
11480
- input = arguments[0];
11481
- } else {
11482
- input = [].slice.call(arguments);
11483
- }
11484
- return normalize(input);
11485
- }
11435
+ // data/data_endpoint.ts
11436
+ import urljoin from "url-join";
11486
11437
 
11487
11438
  // data/data_types.ts
11488
11439
  var DataApiType = /* @__PURE__ */ ((DataApiType2) => {
@@ -11835,6 +11786,33 @@ var EvaluateDatasetJob = class {
11835
11786
  // data/data_endpoint.ts
11836
11787
  var WS_INITIAL_RECONNECT_DELAY = 1e3;
11837
11788
  var WS_MAX_RECONNECT_DELAY = 6e4;
11789
+ function ws_origin(url) {
11790
+ try {
11791
+ return new URL(url).origin;
11792
+ } catch {
11793
+ return url;
11794
+ }
11795
+ }
11796
+ function ws_failure_reason(event, sourceError) {
11797
+ if (sourceError instanceof Error) {
11798
+ return sourceError.message;
11799
+ }
11800
+ if (typeof sourceError === "string") {
11801
+ return sourceError;
11802
+ }
11803
+ return event?.message ?? "";
11804
+ }
11805
+ var EyePopDataWebSocketError = class extends Error {
11806
+ constructor(url, event) {
11807
+ const sourceError = event?.error ?? event;
11808
+ const reason = ws_failure_reason(event, sourceError);
11809
+ super(reason ? `EyePop Dataset event WebSocket connection failed: ${reason}` : "EyePop Dataset event WebSocket connection failed");
11810
+ this.code = "DATA_WEBSOCKET_CONNECTION_FAILED";
11811
+ this.name = "EyePopDataWebSocketError";
11812
+ this.endpoint = ws_origin(url);
11813
+ this.sourceError = sourceError;
11814
+ }
11815
+ };
11838
11816
  function omit_nulls(_, value) {
11839
11817
  if (value === null) {
11840
11818
  return void 0;
@@ -11848,6 +11826,9 @@ var DataEndpoint = class extends Endpoint {
11848
11826
  this.vlmApiUrl = null;
11849
11827
  this._ws = null;
11850
11828
  this._ws_current_reconnect_delay = null;
11829
+ this._ws_reconnect_timeout = null;
11830
+ this._ws_reconnect_enabled = false;
11831
+ this._ws_connecting = null;
11851
11832
  this._accountId = options.accountId || null;
11852
11833
  this._account_event_handlers = [];
11853
11834
  this._dataset_uuid_to_event_handlers = /* @__PURE__ */ new Map();
@@ -11871,14 +11852,15 @@ var DataEndpoint = class extends Endpoint {
11871
11852
  };
11872
11853
  }
11873
11854
  async disconnect(wait = true) {
11855
+ this._ws_reconnect_enabled = false;
11856
+ if (this._ws_reconnect_timeout) {
11857
+ clearTimeout(this._ws_reconnect_timeout);
11858
+ this._ws_reconnect_timeout = null;
11859
+ }
11874
11860
  this.datasetApiUrl = null;
11875
11861
  this.vlmApiUrl = null;
11876
11862
  if (this._ws) {
11877
- try {
11878
- this._ws.close();
11879
- } catch (e) {
11880
- this._logger.warn("ignored exception", e);
11881
- }
11863
+ this.close_ws(this._ws, "unsubscribe");
11882
11864
  this._ws = null;
11883
11865
  }
11884
11866
  await super.disconnect(wait);
@@ -11916,13 +11898,14 @@ var DataEndpoint = class extends Endpoint {
11916
11898
  const vlmApiUrl = new URL(config.vlm_api_url, this.eyepopUrl());
11917
11899
  this.vlmApiUrl = vlmApiUrl.toString();
11918
11900
  if (this.options().disableWs) {
11901
+ this._ws_reconnect_enabled = false;
11919
11902
  this._logger.info("disabled ws connection");
11920
11903
  } else {
11921
11904
  await this.reconnect_ws();
11922
11905
  }
11923
11906
  if (this.options().useCookie) {
11924
11907
  const client = this._client;
11925
- const url = urlJoin(this.datasetApiUrl, "security", "check?set_cookie=true");
11908
+ const url = urljoin(this.datasetApiUrl, "security", "check?set_cookie=true");
11926
11909
  const headers2 = {
11927
11910
  Authorization: await this.authorizationHeader()
11928
11911
  };
@@ -11939,37 +11922,68 @@ var DataEndpoint = class extends Endpoint {
11939
11922
  return Promise.resolve(this);
11940
11923
  }
11941
11924
  async reconnect_ws() {
11925
+ if (this._ws_connecting) {
11926
+ return this._ws_connecting;
11927
+ }
11928
+ const connecting = this.open_ws();
11929
+ this._ws_connecting = connecting;
11930
+ try {
11931
+ await connecting;
11932
+ } finally {
11933
+ this._ws_connecting = null;
11934
+ }
11935
+ }
11936
+ async open_ws() {
11942
11937
  if (this._ws) {
11943
- this._ws.close();
11938
+ this.close_ws(this._ws, "unsubscribe");
11944
11939
  this._ws = null;
11945
11940
  }
11946
11941
  const baseUrl = this.datasetApiUrl?.replace("https://", "wss://").replace("http://", "ws://");
11947
11942
  if (!baseUrl) {
11948
11943
  return;
11949
11944
  }
11950
- const ws_url = urlJoin(baseUrl, "events");
11945
+ const ws_url = urljoin(baseUrl, "events");
11951
11946
  this._requestLogger.debug("about to connect ws to: %s", ws_url);
11952
11947
  return new Promise((resolve, reject) => {
11948
+ let settled = false;
11949
+ const settle_ok = () => {
11950
+ settled = true;
11951
+ resolve();
11952
+ };
11953
+ const settle_err = (error) => {
11954
+ settled = true;
11955
+ reject(error);
11956
+ };
11953
11957
  const ws = createWebSocket(ws_url);
11954
11958
  if (!ws) {
11955
- return resolve();
11959
+ return settle_ok();
11956
11960
  }
11957
11961
  this._ws = ws;
11958
- ws.onopen = async () => {
11959
- const auth_header = await this.authorizationHeader();
11960
- let msg = JSON.stringify({ authorization: auth_header });
11961
- this._requestLogger.debug("ws send: %s", msg);
11962
- ws.send(msg);
11963
- msg = JSON.stringify({ subscribe: { account_uuid: this._accountId } });
11964
- this._requestLogger.debug("ws send: %s", msg);
11965
- ws.send(msg);
11966
- for (let dataset_uuid in this._dataset_uuid_to_event_handlers.keys()) {
11967
- const msg2 = JSON.stringify({ subscribe: { dataset_uuid } });
11968
- this._requestLogger.debug("ws send: %s", msg2);
11969
- ws.send(msg2);
11970
- }
11971
- this._ws_current_reconnect_delay = WS_INITIAL_RECONNECT_DELAY;
11972
- resolve();
11962
+ ws.onopen = () => {
11963
+ void this.authorizationHeader().then((auth_header) => {
11964
+ if (this._ws !== ws) {
11965
+ this.close_ws(ws, "unsubscribe");
11966
+ settle_ok();
11967
+ return;
11968
+ }
11969
+ let msg = JSON.stringify({ authorization: auth_header });
11970
+ this._requestLogger.debug("ws send: %s", msg);
11971
+ ws.send(msg);
11972
+ msg = JSON.stringify({ subscribe: { account_uuid: this._accountId } });
11973
+ this._requestLogger.debug("ws send: %s", msg);
11974
+ ws.send(msg);
11975
+ for (const dataset_uuid of this._dataset_uuid_to_event_handlers.keys()) {
11976
+ const msg2 = JSON.stringify({ subscribe: { dataset_uuid } });
11977
+ this._requestLogger.debug("ws send: %s", msg2);
11978
+ ws.send(msg2);
11979
+ }
11980
+ this._ws_current_reconnect_delay = WS_INITIAL_RECONNECT_DELAY;
11981
+ this._ws_reconnect_enabled = true;
11982
+ settle_ok();
11983
+ }).catch((error) => {
11984
+ this.close_ws(ws, "authorization failed", 1011);
11985
+ settle_err(error);
11986
+ });
11973
11987
  };
11974
11988
  ws.onmessage = (event) => {
11975
11989
  const data = JSON.parse(event.data);
@@ -11980,24 +11994,54 @@ var DataEndpoint = class extends Endpoint {
11980
11994
  ws.onclose = (event) => {
11981
11995
  const { code, reason } = event;
11982
11996
  this._requestLogger.debug("ws closed: %d %s", code, reason);
11983
- if (reason !== "unsubscribe") {
11984
- if (!this._ws_current_reconnect_delay) {
11985
- this._ws_current_reconnect_delay = WS_INITIAL_RECONNECT_DELAY;
11986
- } else if (this._ws_current_reconnect_delay < WS_MAX_RECONNECT_DELAY) {
11987
- this._ws_current_reconnect_delay *= 1.5;
11988
- }
11989
- setTimeout(async () => {
11990
- await this.reconnect_ws();
11991
- }, this._ws_current_reconnect_delay);
11997
+ if (!settled) {
11998
+ settle_err(new EyePopDataWebSocketError(ws_url, new Error(`closed before the connection was established: ${code} ${reason}`)));
11999
+ }
12000
+ if (this._ws !== ws) {
12001
+ return;
11992
12002
  }
12003
+ this._ws = null;
12004
+ this.schedule_ws_reconnect();
11993
12005
  };
11994
12006
  ws.onerror = (event) => {
11995
- this._logger.error(event);
11996
- reject(event);
12007
+ const error = new EyePopDataWebSocketError(ws_url, event);
12008
+ this._logger.error({ err: error }, "dataset event WebSocket connection failed");
12009
+ settle_err(error);
11997
12010
  return null;
11998
12011
  };
11999
12012
  });
12000
12013
  }
12014
+ close_ws(ws, reason, code = 1e3) {
12015
+ try {
12016
+ ws.close(code, reason);
12017
+ } catch (e) {
12018
+ this._logger.warn({ err: e }, "ignored exception closing the dataset event WebSocket");
12019
+ }
12020
+ }
12021
+ schedule_ws_reconnect() {
12022
+ if (!this._ws_reconnect_enabled || this._ws_reconnect_timeout) {
12023
+ return;
12024
+ }
12025
+ if (!this._ws_current_reconnect_delay) {
12026
+ this._ws_current_reconnect_delay = WS_INITIAL_RECONNECT_DELAY;
12027
+ } else if (this._ws_current_reconnect_delay < WS_MAX_RECONNECT_DELAY) {
12028
+ this._ws_current_reconnect_delay *= 1.5;
12029
+ }
12030
+ this._ws_reconnect_timeout = setTimeout(() => {
12031
+ if (!this._ws_reconnect_enabled) {
12032
+ this._ws_reconnect_timeout = null;
12033
+ return;
12034
+ }
12035
+ void this.reconnect_ws().catch((error) => {
12036
+ this._requestLogger.debug("dataset event WebSocket reconnect failed: %s", error);
12037
+ }).finally(() => {
12038
+ this._ws_reconnect_timeout = null;
12039
+ if (!this._ws) {
12040
+ this.schedule_ws_reconnect();
12041
+ }
12042
+ });
12043
+ }, this._ws_current_reconnect_delay);
12044
+ }
12001
12045
  options() {
12002
12046
  return this._options;
12003
12047
  }
@@ -12022,7 +12066,7 @@ var DataEndpoint = class extends Endpoint {
12022
12066
  body: options.body
12023
12067
  };
12024
12068
  this._requestLogger.debug("Request - " + ri.method + " " + path2 + " BODY: " + (ri.body ? ri.body : ""));
12025
- return await client.fetch(urlJoin(baseUrl ?? "", path2), ri);
12069
+ return await client.fetch(urljoin(baseUrl ?? "", path2), ri);
12026
12070
  });
12027
12071
  if (response.status === 204) {
12028
12072
  return null;
@@ -12999,6 +13043,7 @@ export {
12999
13043
  EvaluationStatus,
13000
13044
  ExportedBy,
13001
13045
  EyePop,
13046
+ EyePopDataWebSocketError,
13002
13047
  EyepopLineDecoder,
13003
13048
  ForwardOperatorType,
13004
13049
  InferenceType,