@electric-sql/client 1.1.0 → 1.1.2

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/dist/index.mjs CHANGED
@@ -746,8 +746,9 @@ function canonicalShapeKey(url) {
746
746
  cleanUrl.searchParams.sort();
747
747
  return cleanUrl.toString();
748
748
  }
749
- var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _state, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _isMidStream, _connected, _shapeHandle, _mode, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _activeSnapshotRequests, _midStreamPromise, _midStreamPromiseResolver, _ShapeStream_instances, start_fn, requestShape_fn, constructUrl_fn, createAbortListener_fn, onInitialResponse_fn, onMessages_fn, fetchShape_fn, requestShapeLongPoll_fn, requestShapeSSE_fn, pause_fn, resume_fn, nextTick_fn, waitForStreamEnd_fn, publish_fn, sendErrorToSubscribers_fn, subscribeToVisibilityChanges_fn, reset_fn, fetchSnapshot_fn;
749
+ var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _state, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _isMidStream, _connected, _shapeHandle, _mode, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _activeSnapshotRequests, _midStreamPromise, _midStreamPromiseResolver, _lastSseConnectionStartTime, _minSseConnectionDuration, _consecutiveShortSseConnections, _maxShortSseConnections, _sseFallbackToLongPolling, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _ShapeStream_instances, start_fn, requestShape_fn, constructUrl_fn, createAbortListener_fn, onInitialResponse_fn, onMessages_fn, fetchShape_fn, requestShapeLongPoll_fn, requestShapeSSE_fn, pause_fn, resume_fn, nextTick_fn, waitForStreamEnd_fn, publish_fn, sendErrorToSubscribers_fn, subscribeToVisibilityChanges_fn, reset_fn, fetchSnapshot_fn;
750
750
  var ShapeStream = class {
751
+ // Maximum delay cap (ms)
751
752
  constructor(options) {
752
753
  __privateAdd(this, _ShapeStream_instances);
753
754
  __privateAdd(this, _error, null);
@@ -781,6 +782,16 @@ var ShapeStream = class {
781
782
  // counter for concurrent snapshot requests
782
783
  __privateAdd(this, _midStreamPromise);
783
784
  __privateAdd(this, _midStreamPromiseResolver);
785
+ __privateAdd(this, _lastSseConnectionStartTime);
786
+ __privateAdd(this, _minSseConnectionDuration, 1e3);
787
+ // Minimum expected SSE connection duration (1 second)
788
+ __privateAdd(this, _consecutiveShortSseConnections, 0);
789
+ __privateAdd(this, _maxShortSseConnections, 3);
790
+ // Fall back to long polling after this many short connections
791
+ __privateAdd(this, _sseFallbackToLongPolling, false);
792
+ __privateAdd(this, _sseBackoffBaseDelay, 100);
793
+ // Base delay for exponential backoff (ms)
794
+ __privateAdd(this, _sseBackoffMaxDelay, 5e3);
784
795
  var _a, _b, _c, _d;
785
796
  this.options = __spreadValues({ subscribe: true }, options);
786
797
  validateOptions(this.options);
@@ -956,10 +967,17 @@ _snapshotTracker = new WeakMap();
956
967
  _activeSnapshotRequests = new WeakMap();
957
968
  _midStreamPromise = new WeakMap();
958
969
  _midStreamPromiseResolver = new WeakMap();
970
+ _lastSseConnectionStartTime = new WeakMap();
971
+ _minSseConnectionDuration = new WeakMap();
972
+ _consecutiveShortSseConnections = new WeakMap();
973
+ _maxShortSseConnections = new WeakMap();
974
+ _sseFallbackToLongPolling = new WeakMap();
975
+ _sseBackoffBaseDelay = new WeakMap();
976
+ _sseBackoffMaxDelay = new WeakMap();
959
977
  _ShapeStream_instances = new WeakSet();
960
978
  start_fn = function() {
961
979
  return __async(this, null, function* () {
962
- var _a;
980
+ var _a, _b, _c, _d, _e;
963
981
  __privateSet(this, _started, true);
964
982
  try {
965
983
  yield __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
@@ -967,24 +985,34 @@ start_fn = function() {
967
985
  __privateSet(this, _error, err);
968
986
  if (__privateGet(this, _onError)) {
969
987
  const retryOpts = yield __privateGet(this, _onError).call(this, err);
970
- if (typeof retryOpts === `object`) {
971
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
972
- if (`params` in retryOpts) {
973
- this.options.params = retryOpts.params;
988
+ if (retryOpts && typeof retryOpts === `object`) {
989
+ if (retryOpts.params) {
990
+ this.options.params = __spreadValues(__spreadValues({}, (_a = this.options.params) != null ? _a : {}), retryOpts.params);
974
991
  }
975
- if (`headers` in retryOpts) {
976
- this.options.headers = retryOpts.headers;
992
+ if (retryOpts.headers) {
993
+ this.options.headers = __spreadValues(__spreadValues({}, (_b = this.options.headers) != null ? _b : {}), retryOpts.headers);
977
994
  }
995
+ __privateSet(this, _error, null);
978
996
  __privateSet(this, _started, false);
979
- __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
997
+ yield __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
998
+ return;
999
+ }
1000
+ if (err instanceof Error) {
1001
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
980
1002
  }
1003
+ __privateSet(this, _connected, false);
1004
+ (_c = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _c.call(this);
981
1005
  return;
982
1006
  }
983
- throw err;
984
- } finally {
1007
+ if (err instanceof Error) {
1008
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
1009
+ }
985
1010
  __privateSet(this, _connected, false);
986
- (_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
1011
+ (_d = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _d.call(this);
1012
+ throw err;
987
1013
  }
1014
+ __privateSet(this, _connected, false);
1015
+ (_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
988
1016
  });
989
1017
  };
990
1018
  requestShape_fn = function() {
@@ -1031,7 +1059,6 @@ requestShape_fn = function() {
1031
1059
  yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, Array.isArray(e.json) ? e.json : [e.json]);
1032
1060
  return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
1033
1061
  } else {
1034
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
1035
1062
  throw e;
1036
1063
  }
1037
1064
  } finally {
@@ -1183,7 +1210,7 @@ fetchShape_fn = function(opts) {
1183
1210
  return __async(this, null, function* () {
1184
1211
  var _a;
1185
1212
  const useSse = (_a = this.options.liveSse) != null ? _a : this.options.experimentalLiveSse;
1186
- if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
1213
+ if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause && !__privateGet(this, _sseFallbackToLongPolling)) {
1187
1214
  opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
1188
1215
  opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`);
1189
1216
  return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
@@ -1211,6 +1238,7 @@ requestShapeSSE_fn = function(opts) {
1211
1238
  return __async(this, null, function* () {
1212
1239
  const { fetchUrl, requestAbortController, headers } = opts;
1213
1240
  const fetch2 = __privateGet(this, _sseFetchClient);
1241
+ __privateSet(this, _lastSseConnectionStartTime, Date.now());
1214
1242
  try {
1215
1243
  let buffer = [];
1216
1244
  yield fetchEventSource(fetchUrl.toString(), {
@@ -1244,6 +1272,27 @@ requestShapeSSE_fn = function(opts) {
1244
1272
  throw new FetchBackoffAbortError();
1245
1273
  }
1246
1274
  throw error;
1275
+ } finally {
1276
+ const connectionDuration = Date.now() - __privateGet(this, _lastSseConnectionStartTime);
1277
+ const wasAborted = requestAbortController.signal.aborted;
1278
+ if (connectionDuration < __privateGet(this, _minSseConnectionDuration) && !wasAborted) {
1279
+ __privateWrapper(this, _consecutiveShortSseConnections)._++;
1280
+ if (__privateGet(this, _consecutiveShortSseConnections) >= __privateGet(this, _maxShortSseConnections)) {
1281
+ __privateSet(this, _sseFallbackToLongPolling, true);
1282
+ console.warn(
1283
+ `[Electric] SSE connections are closing immediately (possibly due to proxy buffering or misconfiguration). Falling back to long polling. Your proxy must support streaming SSE responses (not buffer the complete response). Configuration: Nginx add 'X-Accel-Buffering: no', Caddy add 'flush_interval -1' to reverse_proxy. Note: Do NOT disable caching entirely - Electric uses cache headers to enable request collapsing for efficiency.`
1284
+ );
1285
+ } else {
1286
+ const maxDelay = Math.min(
1287
+ __privateGet(this, _sseBackoffMaxDelay),
1288
+ __privateGet(this, _sseBackoffBaseDelay) * Math.pow(2, __privateGet(this, _consecutiveShortSseConnections))
1289
+ );
1290
+ const delayMs = Math.floor(Math.random() * maxDelay);
1291
+ yield new Promise((resolve) => setTimeout(resolve, delayMs));
1292
+ }
1293
+ } else if (connectionDuration >= __privateGet(this, _minSseConnectionDuration)) {
1294
+ __privateSet(this, _consecutiveShortSseConnections, 0);
1295
+ }
1247
1296
  }
1248
1297
  });
1249
1298
  };
@@ -1342,6 +1391,8 @@ reset_fn = function(handle) {
1342
1391
  __privateSet(this, _connected, false);
1343
1392
  __privateSet(this, _schema, void 0);
1344
1393
  __privateSet(this, _activeSnapshotRequests, 0);
1394
+ __privateSet(this, _consecutiveShortSseConnections, 0);
1395
+ __privateSet(this, _sseFallbackToLongPolling, false);
1345
1396
  };
1346
1397
  fetchSnapshot_fn = function(url, headers) {
1347
1398
  return __async(this, null, function* () {