@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.
@@ -716,8 +716,9 @@ function canonicalShapeKey(url) {
716
716
  cleanUrl.searchParams.sort();
717
717
  return cleanUrl.toString();
718
718
  }
719
- 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;
719
+ 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;
720
720
  var ShapeStream = class {
721
+ // Maximum delay cap (ms)
721
722
  constructor(options) {
722
723
  __privateAdd(this, _ShapeStream_instances);
723
724
  __privateAdd(this, _error, null);
@@ -751,6 +752,16 @@ var ShapeStream = class {
751
752
  // counter for concurrent snapshot requests
752
753
  __privateAdd(this, _midStreamPromise);
753
754
  __privateAdd(this, _midStreamPromiseResolver);
755
+ __privateAdd(this, _lastSseConnectionStartTime);
756
+ __privateAdd(this, _minSseConnectionDuration, 1e3);
757
+ // Minimum expected SSE connection duration (1 second)
758
+ __privateAdd(this, _consecutiveShortSseConnections, 0);
759
+ __privateAdd(this, _maxShortSseConnections, 3);
760
+ // Fall back to long polling after this many short connections
761
+ __privateAdd(this, _sseFallbackToLongPolling, false);
762
+ __privateAdd(this, _sseBackoffBaseDelay, 100);
763
+ // Base delay for exponential backoff (ms)
764
+ __privateAdd(this, _sseBackoffMaxDelay, 5e3);
754
765
  var _a, _b, _c, _d;
755
766
  this.options = __spreadValues({ subscribe: true }, options);
756
767
  validateOptions(this.options);
@@ -922,9 +933,16 @@ _snapshotTracker = new WeakMap();
922
933
  _activeSnapshotRequests = new WeakMap();
923
934
  _midStreamPromise = new WeakMap();
924
935
  _midStreamPromiseResolver = new WeakMap();
936
+ _lastSseConnectionStartTime = new WeakMap();
937
+ _minSseConnectionDuration = new WeakMap();
938
+ _consecutiveShortSseConnections = new WeakMap();
939
+ _maxShortSseConnections = new WeakMap();
940
+ _sseFallbackToLongPolling = new WeakMap();
941
+ _sseBackoffBaseDelay = new WeakMap();
942
+ _sseBackoffMaxDelay = new WeakMap();
925
943
  _ShapeStream_instances = new WeakSet();
926
944
  start_fn = async function() {
927
- var _a;
945
+ var _a, _b, _c, _d, _e;
928
946
  __privateSet(this, _started, true);
929
947
  try {
930
948
  await __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
@@ -932,24 +950,34 @@ start_fn = async function() {
932
950
  __privateSet(this, _error, err);
933
951
  if (__privateGet(this, _onError)) {
934
952
  const retryOpts = await __privateGet(this, _onError).call(this, err);
935
- if (typeof retryOpts === `object`) {
936
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
937
- if (`params` in retryOpts) {
938
- this.options.params = retryOpts.params;
953
+ if (retryOpts && typeof retryOpts === `object`) {
954
+ if (retryOpts.params) {
955
+ this.options.params = __spreadValues(__spreadValues({}, (_a = this.options.params) != null ? _a : {}), retryOpts.params);
939
956
  }
940
- if (`headers` in retryOpts) {
941
- this.options.headers = retryOpts.headers;
957
+ if (retryOpts.headers) {
958
+ this.options.headers = __spreadValues(__spreadValues({}, (_b = this.options.headers) != null ? _b : {}), retryOpts.headers);
942
959
  }
960
+ __privateSet(this, _error, null);
943
961
  __privateSet(this, _started, false);
944
- __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
962
+ await __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
963
+ return;
964
+ }
965
+ if (err instanceof Error) {
966
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
945
967
  }
968
+ __privateSet(this, _connected, false);
969
+ (_c = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _c.call(this);
946
970
  return;
947
971
  }
948
- throw err;
949
- } finally {
972
+ if (err instanceof Error) {
973
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
974
+ }
950
975
  __privateSet(this, _connected, false);
951
- (_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
976
+ (_d = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _d.call(this);
977
+ throw err;
952
978
  }
979
+ __privateSet(this, _connected, false);
980
+ (_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
953
981
  };
954
982
  requestShape_fn = async function() {
955
983
  var _a, _b;
@@ -994,7 +1022,6 @@ requestShape_fn = async function() {
994
1022
  await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, Array.isArray(e.json) ? e.json : [e.json]);
995
1023
  return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
996
1024
  } else {
997
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
998
1025
  throw e;
999
1026
  }
1000
1027
  } finally {
@@ -1136,7 +1163,7 @@ onMessages_fn = async function(batch, isSseMessage = false) {
1136
1163
  fetchShape_fn = async function(opts) {
1137
1164
  var _a;
1138
1165
  const useSse = (_a = this.options.liveSse) != null ? _a : this.options.experimentalLiveSse;
1139
- if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
1166
+ if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause && !__privateGet(this, _sseFallbackToLongPolling)) {
1140
1167
  opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
1141
1168
  opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`);
1142
1169
  return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
@@ -1160,6 +1187,7 @@ requestShapeLongPoll_fn = async function(opts) {
1160
1187
  requestShapeSSE_fn = async function(opts) {
1161
1188
  const { fetchUrl, requestAbortController, headers } = opts;
1162
1189
  const fetch2 = __privateGet(this, _sseFetchClient);
1190
+ __privateSet(this, _lastSseConnectionStartTime, Date.now());
1163
1191
  try {
1164
1192
  let buffer = [];
1165
1193
  await fetchEventSource(fetchUrl.toString(), {
@@ -1193,6 +1221,27 @@ requestShapeSSE_fn = async function(opts) {
1193
1221
  throw new FetchBackoffAbortError();
1194
1222
  }
1195
1223
  throw error;
1224
+ } finally {
1225
+ const connectionDuration = Date.now() - __privateGet(this, _lastSseConnectionStartTime);
1226
+ const wasAborted = requestAbortController.signal.aborted;
1227
+ if (connectionDuration < __privateGet(this, _minSseConnectionDuration) && !wasAborted) {
1228
+ __privateWrapper(this, _consecutiveShortSseConnections)._++;
1229
+ if (__privateGet(this, _consecutiveShortSseConnections) >= __privateGet(this, _maxShortSseConnections)) {
1230
+ __privateSet(this, _sseFallbackToLongPolling, true);
1231
+ console.warn(
1232
+ `[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.`
1233
+ );
1234
+ } else {
1235
+ const maxDelay = Math.min(
1236
+ __privateGet(this, _sseBackoffMaxDelay),
1237
+ __privateGet(this, _sseBackoffBaseDelay) * Math.pow(2, __privateGet(this, _consecutiveShortSseConnections))
1238
+ );
1239
+ const delayMs = Math.floor(Math.random() * maxDelay);
1240
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1241
+ }
1242
+ } else if (connectionDuration >= __privateGet(this, _minSseConnectionDuration)) {
1243
+ __privateSet(this, _consecutiveShortSseConnections, 0);
1244
+ }
1196
1245
  }
1197
1246
  };
1198
1247
  pause_fn = function() {
@@ -1284,6 +1333,8 @@ reset_fn = function(handle) {
1284
1333
  __privateSet(this, _connected, false);
1285
1334
  __privateSet(this, _schema, void 0);
1286
1335
  __privateSet(this, _activeSnapshotRequests, 0);
1336
+ __privateSet(this, _consecutiveShortSseConnections, 0);
1337
+ __privateSet(this, _sseFallbackToLongPolling, false);
1287
1338
  };
1288
1339
  fetchSnapshot_fn = async function(url, headers) {
1289
1340
  const response = await __privateGet(this, _fetchClient2).call(this, url.toString(), { headers });