@schematichq/schematic-react 1.2.12 → 1.2.13

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.
@@ -799,7 +799,7 @@ function contextString(context) {
799
799
  }, {});
800
800
  return JSON.stringify(sortedContext);
801
801
  }
802
- var version = "1.2.12";
802
+ var version = "1.2.13";
803
803
  var anonymousIdKey = "schematicId";
804
804
  var Schematic = class {
805
805
  additionalHeaders = {};
@@ -824,6 +824,9 @@ var Schematic = class {
824
824
  webSocketConnectionTimeout = 1e4;
825
825
  webSocketReconnect = true;
826
826
  webSocketMaxReconnectAttempts = 7;
827
+ // Max attempts after connection disrupted
828
+ webSocketMaxConnectionAttempts = 3;
829
+ // Max attempts for initial connection
827
830
  webSocketInitialRetryDelay = 1e3;
828
831
  webSocketMaxRetryDelay = 3e4;
829
832
  wsReconnectAttempts = 0;
@@ -1047,7 +1050,7 @@ var Schematic = class {
1047
1050
  this.submitFlagCheckEvent(key, result, context);
1048
1051
  return result.value;
1049
1052
  }).catch((error) => {
1050
- console.error("There was a problem with the fetch operation:", error);
1053
+ console.warn("There was a problem with the fetch operation:", error);
1051
1054
  const errorResult = this.resolveFallbackCheckFlagReturn(
1052
1055
  key,
1053
1056
  fallback,
@@ -1070,10 +1073,7 @@ var Schematic = class {
1070
1073
  try {
1071
1074
  await this.setContext(context);
1072
1075
  } catch (error) {
1073
- console.error(
1074
- "WebSocket connection failed, falling back to REST:",
1075
- error
1076
- );
1076
+ console.warn("WebSocket connection failed, falling back to REST:", error);
1077
1077
  return this.fallbackToRest(key, context, fallback);
1078
1078
  }
1079
1079
  const contextVals = this.checks[contextStr] ?? {};
@@ -1213,7 +1213,7 @@ var Schematic = class {
1213
1213
  this.submitFlagCheckEvent(key, result, context);
1214
1214
  return result.value;
1215
1215
  } catch (error) {
1216
- console.error("REST API call failed, using fallback value:", error);
1216
+ console.warn("REST API call failed, using fallback value:", error);
1217
1217
  const errorResult = this.resolveFallbackCheckFlagReturn(
1218
1218
  key,
1219
1219
  fallback,
@@ -1262,7 +1262,7 @@ var Schematic = class {
1262
1262
  {}
1263
1263
  );
1264
1264
  }).catch((error) => {
1265
- console.error("There was a problem with the fetch operation:", error);
1265
+ console.warn("There was a problem with the fetch operation:", error);
1266
1266
  return {};
1267
1267
  });
1268
1268
  };
@@ -1279,7 +1279,7 @@ var Schematic = class {
1279
1279
  user: body.keys
1280
1280
  });
1281
1281
  } catch (error) {
1282
- console.error("Error setting context:", error);
1282
+ console.warn("Error setting context:", error);
1283
1283
  }
1284
1284
  return this.handleEvent("identify", body);
1285
1285
  };
@@ -1337,7 +1337,7 @@ var Schematic = class {
1337
1337
  const socket = await this.conn;
1338
1338
  await this.wsSendMessage(socket, context);
1339
1339
  } catch (error) {
1340
- console.error("Failed to establish WebSocket connection:", error);
1340
+ console.warn("Failed to establish WebSocket connection:", error);
1341
1341
  throw error;
1342
1342
  }
1343
1343
  };
@@ -1758,7 +1758,7 @@ var Schematic = class {
1758
1758
  }
1759
1759
  socket.close();
1760
1760
  } catch (error) {
1761
- console.error("Error during cleanup:", error);
1761
+ console.warn("Error during cleanup:", error);
1762
1762
  } finally {
1763
1763
  this.conn = null;
1764
1764
  this.currentWebSocket = null;
@@ -1901,14 +1901,49 @@ var Schematic = class {
1901
1901
  }
1902
1902
  }, delay);
1903
1903
  };
1904
- // Open a websocket connection
1905
- wsConnect = () => {
1904
+ // Open a websocket connection with retry logic for timeouts
1905
+ wsConnect = async () => {
1906
1906
  if (this.isOffline()) {
1907
1907
  this.debug("wsConnect: skipped (offline mode)");
1908
- return Promise.reject(
1909
- new Error("WebSocket connection skipped in offline mode")
1910
- );
1908
+ throw new Error("WebSocket connection skipped in offline mode");
1909
+ }
1910
+ let lastError = null;
1911
+ const maxAttempts = this.webSocketMaxConnectionAttempts;
1912
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1913
+ try {
1914
+ const socket = await this.wsConnectOnce();
1915
+ this.wsReconnectAttempts = 0;
1916
+ return socket;
1917
+ } catch (error) {
1918
+ lastError = error instanceof Error ? error : new Error(String(error));
1919
+ const isTimeout = lastError.message === "WebSocket connection timeout";
1920
+ if (!isTimeout) {
1921
+ this.debug(
1922
+ `WebSocket connection failed with non-timeout error, not retrying:`,
1923
+ lastError.message
1924
+ );
1925
+ throw lastError;
1926
+ }
1927
+ if (attempt < maxAttempts - 1) {
1928
+ const baseDelay = this.webSocketInitialRetryDelay * Math.pow(2, attempt);
1929
+ const cappedDelay = Math.min(baseDelay, this.webSocketMaxRetryDelay);
1930
+ const jitter = cappedDelay * 0.2 * Math.random();
1931
+ const delay = cappedDelay + jitter;
1932
+ this.debug(
1933
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), retrying in ${delay.toFixed(0)}ms`
1934
+ );
1935
+ await new Promise((resolve) => setTimeout(resolve, delay));
1936
+ } else {
1937
+ this.debug(
1938
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), no more retries`
1939
+ );
1940
+ }
1941
+ }
1911
1942
  }
1943
+ throw lastError ?? new Error("WebSocket connection failed");
1944
+ };
1945
+ // Single attempt to open a websocket connection (no retry logic)
1946
+ wsConnectOnce = () => {
1912
1947
  return new Promise((resolve, reject) => {
1913
1948
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1914
1949
  this.debug(`connecting to WebSocket:`, wsUrl);
@@ -1919,6 +1954,7 @@ var Schematic = class {
1919
1954
  let isResolved = false;
1920
1955
  timeoutId = setTimeout(() => {
1921
1956
  if (!isResolved) {
1957
+ isResolved = true;
1922
1958
  this.debug(
1923
1959
  `WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`
1924
1960
  );
@@ -1927,16 +1963,17 @@ var Schematic = class {
1927
1963
  }
1928
1964
  }, this.webSocketConnectionTimeout);
1929
1965
  webSocket.onopen = () => {
1966
+ if (isResolved) return;
1930
1967
  isResolved = true;
1931
1968
  if (timeoutId !== null) {
1932
1969
  clearTimeout(timeoutId);
1933
1970
  }
1934
- this.wsReconnectAttempts = 0;
1935
1971
  this.wsIntentionalDisconnect = false;
1936
1972
  this.debug(`WebSocket connection ${connectionId} opened successfully`);
1937
1973
  resolve(webSocket);
1938
1974
  };
1939
1975
  webSocket.onerror = (error) => {
1976
+ if (isResolved) return;
1940
1977
  isResolved = true;
1941
1978
  if (timeoutId !== null) {
1942
1979
  clearTimeout(timeoutId);
@@ -1945,7 +1982,6 @@ var Schematic = class {
1945
1982
  reject(error);
1946
1983
  };
1947
1984
  webSocket.onclose = () => {
1948
- isResolved = true;
1949
1985
  if (timeoutId !== null) {
1950
1986
  clearTimeout(timeoutId);
1951
1987
  }
@@ -1955,7 +1991,7 @@ var Schematic = class {
1955
1991
  this.currentWebSocket = null;
1956
1992
  this.isConnecting = false;
1957
1993
  }
1958
- if (!this.wsIntentionalDisconnect && this.webSocketReconnect) {
1994
+ if (!isResolved && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
1959
1995
  this.attemptReconnect();
1960
1996
  }
1961
1997
  };
@@ -2137,7 +2173,7 @@ var notifyFlagValueListener = (listener, value) => {
2137
2173
  var import_react = __toESM(require("react"));
2138
2174
 
2139
2175
  // src/version.ts
2140
- var version2 = "1.2.12";
2176
+ var version2 = "1.2.13";
2141
2177
 
2142
2178
  // src/context/schematic.tsx
2143
2179
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -754,7 +754,7 @@ function contextString(context) {
754
754
  }, {});
755
755
  return JSON.stringify(sortedContext);
756
756
  }
757
- var version = "1.2.12";
757
+ var version = "1.2.13";
758
758
  var anonymousIdKey = "schematicId";
759
759
  var Schematic = class {
760
760
  additionalHeaders = {};
@@ -779,6 +779,9 @@ var Schematic = class {
779
779
  webSocketConnectionTimeout = 1e4;
780
780
  webSocketReconnect = true;
781
781
  webSocketMaxReconnectAttempts = 7;
782
+ // Max attempts after connection disrupted
783
+ webSocketMaxConnectionAttempts = 3;
784
+ // Max attempts for initial connection
782
785
  webSocketInitialRetryDelay = 1e3;
783
786
  webSocketMaxRetryDelay = 3e4;
784
787
  wsReconnectAttempts = 0;
@@ -1002,7 +1005,7 @@ var Schematic = class {
1002
1005
  this.submitFlagCheckEvent(key, result, context);
1003
1006
  return result.value;
1004
1007
  }).catch((error) => {
1005
- console.error("There was a problem with the fetch operation:", error);
1008
+ console.warn("There was a problem with the fetch operation:", error);
1006
1009
  const errorResult = this.resolveFallbackCheckFlagReturn(
1007
1010
  key,
1008
1011
  fallback,
@@ -1025,10 +1028,7 @@ var Schematic = class {
1025
1028
  try {
1026
1029
  await this.setContext(context);
1027
1030
  } catch (error) {
1028
- console.error(
1029
- "WebSocket connection failed, falling back to REST:",
1030
- error
1031
- );
1031
+ console.warn("WebSocket connection failed, falling back to REST:", error);
1032
1032
  return this.fallbackToRest(key, context, fallback);
1033
1033
  }
1034
1034
  const contextVals = this.checks[contextStr] ?? {};
@@ -1168,7 +1168,7 @@ var Schematic = class {
1168
1168
  this.submitFlagCheckEvent(key, result, context);
1169
1169
  return result.value;
1170
1170
  } catch (error) {
1171
- console.error("REST API call failed, using fallback value:", error);
1171
+ console.warn("REST API call failed, using fallback value:", error);
1172
1172
  const errorResult = this.resolveFallbackCheckFlagReturn(
1173
1173
  key,
1174
1174
  fallback,
@@ -1217,7 +1217,7 @@ var Schematic = class {
1217
1217
  {}
1218
1218
  );
1219
1219
  }).catch((error) => {
1220
- console.error("There was a problem with the fetch operation:", error);
1220
+ console.warn("There was a problem with the fetch operation:", error);
1221
1221
  return {};
1222
1222
  });
1223
1223
  };
@@ -1234,7 +1234,7 @@ var Schematic = class {
1234
1234
  user: body.keys
1235
1235
  });
1236
1236
  } catch (error) {
1237
- console.error("Error setting context:", error);
1237
+ console.warn("Error setting context:", error);
1238
1238
  }
1239
1239
  return this.handleEvent("identify", body);
1240
1240
  };
@@ -1292,7 +1292,7 @@ var Schematic = class {
1292
1292
  const socket = await this.conn;
1293
1293
  await this.wsSendMessage(socket, context);
1294
1294
  } catch (error) {
1295
- console.error("Failed to establish WebSocket connection:", error);
1295
+ console.warn("Failed to establish WebSocket connection:", error);
1296
1296
  throw error;
1297
1297
  }
1298
1298
  };
@@ -1713,7 +1713,7 @@ var Schematic = class {
1713
1713
  }
1714
1714
  socket.close();
1715
1715
  } catch (error) {
1716
- console.error("Error during cleanup:", error);
1716
+ console.warn("Error during cleanup:", error);
1717
1717
  } finally {
1718
1718
  this.conn = null;
1719
1719
  this.currentWebSocket = null;
@@ -1856,14 +1856,49 @@ var Schematic = class {
1856
1856
  }
1857
1857
  }, delay);
1858
1858
  };
1859
- // Open a websocket connection
1860
- wsConnect = () => {
1859
+ // Open a websocket connection with retry logic for timeouts
1860
+ wsConnect = async () => {
1861
1861
  if (this.isOffline()) {
1862
1862
  this.debug("wsConnect: skipped (offline mode)");
1863
- return Promise.reject(
1864
- new Error("WebSocket connection skipped in offline mode")
1865
- );
1863
+ throw new Error("WebSocket connection skipped in offline mode");
1864
+ }
1865
+ let lastError = null;
1866
+ const maxAttempts = this.webSocketMaxConnectionAttempts;
1867
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1868
+ try {
1869
+ const socket = await this.wsConnectOnce();
1870
+ this.wsReconnectAttempts = 0;
1871
+ return socket;
1872
+ } catch (error) {
1873
+ lastError = error instanceof Error ? error : new Error(String(error));
1874
+ const isTimeout = lastError.message === "WebSocket connection timeout";
1875
+ if (!isTimeout) {
1876
+ this.debug(
1877
+ `WebSocket connection failed with non-timeout error, not retrying:`,
1878
+ lastError.message
1879
+ );
1880
+ throw lastError;
1881
+ }
1882
+ if (attempt < maxAttempts - 1) {
1883
+ const baseDelay = this.webSocketInitialRetryDelay * Math.pow(2, attempt);
1884
+ const cappedDelay = Math.min(baseDelay, this.webSocketMaxRetryDelay);
1885
+ const jitter = cappedDelay * 0.2 * Math.random();
1886
+ const delay = cappedDelay + jitter;
1887
+ this.debug(
1888
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), retrying in ${delay.toFixed(0)}ms`
1889
+ );
1890
+ await new Promise((resolve) => setTimeout(resolve, delay));
1891
+ } else {
1892
+ this.debug(
1893
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), no more retries`
1894
+ );
1895
+ }
1896
+ }
1866
1897
  }
1898
+ throw lastError ?? new Error("WebSocket connection failed");
1899
+ };
1900
+ // Single attempt to open a websocket connection (no retry logic)
1901
+ wsConnectOnce = () => {
1867
1902
  return new Promise((resolve, reject) => {
1868
1903
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1869
1904
  this.debug(`connecting to WebSocket:`, wsUrl);
@@ -1874,6 +1909,7 @@ var Schematic = class {
1874
1909
  let isResolved = false;
1875
1910
  timeoutId = setTimeout(() => {
1876
1911
  if (!isResolved) {
1912
+ isResolved = true;
1877
1913
  this.debug(
1878
1914
  `WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`
1879
1915
  );
@@ -1882,16 +1918,17 @@ var Schematic = class {
1882
1918
  }
1883
1919
  }, this.webSocketConnectionTimeout);
1884
1920
  webSocket.onopen = () => {
1921
+ if (isResolved) return;
1885
1922
  isResolved = true;
1886
1923
  if (timeoutId !== null) {
1887
1924
  clearTimeout(timeoutId);
1888
1925
  }
1889
- this.wsReconnectAttempts = 0;
1890
1926
  this.wsIntentionalDisconnect = false;
1891
1927
  this.debug(`WebSocket connection ${connectionId} opened successfully`);
1892
1928
  resolve(webSocket);
1893
1929
  };
1894
1930
  webSocket.onerror = (error) => {
1931
+ if (isResolved) return;
1895
1932
  isResolved = true;
1896
1933
  if (timeoutId !== null) {
1897
1934
  clearTimeout(timeoutId);
@@ -1900,7 +1937,6 @@ var Schematic = class {
1900
1937
  reject(error);
1901
1938
  };
1902
1939
  webSocket.onclose = () => {
1903
- isResolved = true;
1904
1940
  if (timeoutId !== null) {
1905
1941
  clearTimeout(timeoutId);
1906
1942
  }
@@ -1910,7 +1946,7 @@ var Schematic = class {
1910
1946
  this.currentWebSocket = null;
1911
1947
  this.isConnecting = false;
1912
1948
  }
1913
- if (!this.wsIntentionalDisconnect && this.webSocketReconnect) {
1949
+ if (!isResolved && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
1914
1950
  this.attemptReconnect();
1915
1951
  }
1916
1952
  };
@@ -2092,7 +2128,7 @@ var notifyFlagValueListener = (listener, value) => {
2092
2128
  import React, { createContext, useEffect, useMemo, useRef } from "react";
2093
2129
 
2094
2130
  // src/version.ts
2095
- var version2 = "1.2.12";
2131
+ var version2 = "1.2.13";
2096
2132
 
2097
2133
  // src/context/schematic.tsx
2098
2134
  import { jsx } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematichq/schematic-react",
3
- "version": "1.2.12",
3
+ "version": "1.2.13",
4
4
  "main": "dist/schematic-react.cjs.js",
5
5
  "module": "dist/schematic-react.esm.js",
6
6
  "types": "dist/schematic-react.d.ts",
@@ -31,7 +31,7 @@
31
31
  "prepare": "husky"
32
32
  },
33
33
  "dependencies": {
34
- "@schematichq/schematic-js": "^1.2.12"
34
+ "@schematichq/schematic-js": "^1.2.13"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@eslint/js": "^9.39.1",