@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.
@@ -775,8 +775,9 @@ function canonicalShapeKey(url) {
775
775
  cleanUrl.searchParams.sort();
776
776
  return cleanUrl.toString();
777
777
  }
778
- 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;
778
+ 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;
779
779
  var ShapeStream = class {
780
+ // Maximum delay cap (ms)
780
781
  constructor(options) {
781
782
  __privateAdd(this, _ShapeStream_instances);
782
783
  __privateAdd(this, _error, null);
@@ -810,6 +811,16 @@ var ShapeStream = class {
810
811
  // counter for concurrent snapshot requests
811
812
  __privateAdd(this, _midStreamPromise);
812
813
  __privateAdd(this, _midStreamPromiseResolver);
814
+ __privateAdd(this, _lastSseConnectionStartTime);
815
+ __privateAdd(this, _minSseConnectionDuration, 1e3);
816
+ // Minimum expected SSE connection duration (1 second)
817
+ __privateAdd(this, _consecutiveShortSseConnections, 0);
818
+ __privateAdd(this, _maxShortSseConnections, 3);
819
+ // Fall back to long polling after this many short connections
820
+ __privateAdd(this, _sseFallbackToLongPolling, false);
821
+ __privateAdd(this, _sseBackoffBaseDelay, 100);
822
+ // Base delay for exponential backoff (ms)
823
+ __privateAdd(this, _sseBackoffMaxDelay, 5e3);
813
824
  var _a, _b, _c, _d;
814
825
  this.options = __spreadValues({ subscribe: true }, options);
815
826
  validateOptions(this.options);
@@ -985,10 +996,17 @@ _snapshotTracker = new WeakMap();
985
996
  _activeSnapshotRequests = new WeakMap();
986
997
  _midStreamPromise = new WeakMap();
987
998
  _midStreamPromiseResolver = new WeakMap();
999
+ _lastSseConnectionStartTime = new WeakMap();
1000
+ _minSseConnectionDuration = new WeakMap();
1001
+ _consecutiveShortSseConnections = new WeakMap();
1002
+ _maxShortSseConnections = new WeakMap();
1003
+ _sseFallbackToLongPolling = new WeakMap();
1004
+ _sseBackoffBaseDelay = new WeakMap();
1005
+ _sseBackoffMaxDelay = new WeakMap();
988
1006
  _ShapeStream_instances = new WeakSet();
989
1007
  start_fn = function() {
990
1008
  return __async(this, null, function* () {
991
- var _a;
1009
+ var _a, _b, _c, _d, _e;
992
1010
  __privateSet(this, _started, true);
993
1011
  try {
994
1012
  yield __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
@@ -996,24 +1014,34 @@ start_fn = function() {
996
1014
  __privateSet(this, _error, err);
997
1015
  if (__privateGet(this, _onError)) {
998
1016
  const retryOpts = yield __privateGet(this, _onError).call(this, err);
999
- if (typeof retryOpts === `object`) {
1000
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
1001
- if (`params` in retryOpts) {
1002
- this.options.params = retryOpts.params;
1017
+ if (retryOpts && typeof retryOpts === `object`) {
1018
+ if (retryOpts.params) {
1019
+ this.options.params = __spreadValues(__spreadValues({}, (_a = this.options.params) != null ? _a : {}), retryOpts.params);
1003
1020
  }
1004
- if (`headers` in retryOpts) {
1005
- this.options.headers = retryOpts.headers;
1021
+ if (retryOpts.headers) {
1022
+ this.options.headers = __spreadValues(__spreadValues({}, (_b = this.options.headers) != null ? _b : {}), retryOpts.headers);
1006
1023
  }
1024
+ __privateSet(this, _error, null);
1007
1025
  __privateSet(this, _started, false);
1008
- __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
1026
+ yield __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
1027
+ return;
1028
+ }
1029
+ if (err instanceof Error) {
1030
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
1009
1031
  }
1032
+ __privateSet(this, _connected, false);
1033
+ (_c = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _c.call(this);
1010
1034
  return;
1011
1035
  }
1012
- throw err;
1013
- } finally {
1036
+ if (err instanceof Error) {
1037
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
1038
+ }
1014
1039
  __privateSet(this, _connected, false);
1015
- (_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
1040
+ (_d = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _d.call(this);
1041
+ throw err;
1016
1042
  }
1043
+ __privateSet(this, _connected, false);
1044
+ (_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
1017
1045
  });
1018
1046
  };
1019
1047
  requestShape_fn = function() {
@@ -1060,7 +1088,6 @@ requestShape_fn = function() {
1060
1088
  yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, Array.isArray(e.json) ? e.json : [e.json]);
1061
1089
  return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
1062
1090
  } else {
1063
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
1064
1091
  throw e;
1065
1092
  }
1066
1093
  } finally {
@@ -1212,7 +1239,7 @@ fetchShape_fn = function(opts) {
1212
1239
  return __async(this, null, function* () {
1213
1240
  var _a;
1214
1241
  const useSse = (_a = this.options.liveSse) != null ? _a : this.options.experimentalLiveSse;
1215
- if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
1242
+ if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause && !__privateGet(this, _sseFallbackToLongPolling)) {
1216
1243
  opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
1217
1244
  opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`);
1218
1245
  return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
@@ -1240,6 +1267,7 @@ requestShapeSSE_fn = function(opts) {
1240
1267
  return __async(this, null, function* () {
1241
1268
  const { fetchUrl, requestAbortController, headers } = opts;
1242
1269
  const fetch2 = __privateGet(this, _sseFetchClient);
1270
+ __privateSet(this, _lastSseConnectionStartTime, Date.now());
1243
1271
  try {
1244
1272
  let buffer = [];
1245
1273
  yield (0, import_fetch_event_source.fetchEventSource)(fetchUrl.toString(), {
@@ -1273,6 +1301,27 @@ requestShapeSSE_fn = function(opts) {
1273
1301
  throw new FetchBackoffAbortError();
1274
1302
  }
1275
1303
  throw error;
1304
+ } finally {
1305
+ const connectionDuration = Date.now() - __privateGet(this, _lastSseConnectionStartTime);
1306
+ const wasAborted = requestAbortController.signal.aborted;
1307
+ if (connectionDuration < __privateGet(this, _minSseConnectionDuration) && !wasAborted) {
1308
+ __privateWrapper(this, _consecutiveShortSseConnections)._++;
1309
+ if (__privateGet(this, _consecutiveShortSseConnections) >= __privateGet(this, _maxShortSseConnections)) {
1310
+ __privateSet(this, _sseFallbackToLongPolling, true);
1311
+ console.warn(
1312
+ `[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.`
1313
+ );
1314
+ } else {
1315
+ const maxDelay = Math.min(
1316
+ __privateGet(this, _sseBackoffMaxDelay),
1317
+ __privateGet(this, _sseBackoffBaseDelay) * Math.pow(2, __privateGet(this, _consecutiveShortSseConnections))
1318
+ );
1319
+ const delayMs = Math.floor(Math.random() * maxDelay);
1320
+ yield new Promise((resolve) => setTimeout(resolve, delayMs));
1321
+ }
1322
+ } else if (connectionDuration >= __privateGet(this, _minSseConnectionDuration)) {
1323
+ __privateSet(this, _consecutiveShortSseConnections, 0);
1324
+ }
1276
1325
  }
1277
1326
  });
1278
1327
  };
@@ -1371,6 +1420,8 @@ reset_fn = function(handle) {
1371
1420
  __privateSet(this, _connected, false);
1372
1421
  __privateSet(this, _schema, void 0);
1373
1422
  __privateSet(this, _activeSnapshotRequests, 0);
1423
+ __privateSet(this, _consecutiveShortSseConnections, 0);
1424
+ __privateSet(this, _sseFallbackToLongPolling, false);
1374
1425
  };
1375
1426
  fetchSnapshot_fn = function(url, headers) {
1376
1427
  return __async(this, null, function* () {