@electric-sql/client 1.1.2 → 1.1.4
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/README.md +74 -12
- package/dist/cjs/index.cjs +58 -12
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +53 -4
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +53 -4
- package/dist/index.legacy-esm.js +58 -12
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +58 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +70 -6
- package/src/constants.ts +1 -0
- package/src/fetch.ts +75 -9
package/dist/index.mjs
CHANGED
|
@@ -322,6 +322,7 @@ var SUBSET_PARAM_ORDER_BY = `subset__order_by`;
|
|
|
322
322
|
var SUBSET_PARAM_WHERE_PARAMS = `subset__params`;
|
|
323
323
|
var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
324
324
|
LIVE_QUERY_PARAM,
|
|
325
|
+
LIVE_SSE_QUERY_PARAM,
|
|
325
326
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
326
327
|
OFFSET_QUERY_PARAM,
|
|
327
328
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
@@ -338,16 +339,33 @@ var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
|
338
339
|
var HTTP_RETRY_STATUS_CODES = [429];
|
|
339
340
|
var BackoffDefaults = {
|
|
340
341
|
initialDelay: 100,
|
|
341
|
-
maxDelay:
|
|
342
|
-
|
|
342
|
+
maxDelay: 6e4,
|
|
343
|
+
// Cap at 60s - reasonable for long-lived connections
|
|
344
|
+
multiplier: 1.3,
|
|
345
|
+
maxRetries: Infinity
|
|
346
|
+
// Retry forever - clients may go offline and come back
|
|
343
347
|
};
|
|
348
|
+
function parseRetryAfterHeader(retryAfter) {
|
|
349
|
+
if (!retryAfter) return 0;
|
|
350
|
+
const retryAfterSec = Number(retryAfter);
|
|
351
|
+
if (Number.isFinite(retryAfterSec) && retryAfterSec > 0) {
|
|
352
|
+
return retryAfterSec * 1e3;
|
|
353
|
+
}
|
|
354
|
+
const retryDate = Date.parse(retryAfter);
|
|
355
|
+
if (!isNaN(retryDate)) {
|
|
356
|
+
const deltaMs = retryDate - Date.now();
|
|
357
|
+
return Math.max(0, Math.min(deltaMs, 36e5));
|
|
358
|
+
}
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
344
361
|
function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
345
362
|
const {
|
|
346
363
|
initialDelay,
|
|
347
364
|
maxDelay,
|
|
348
365
|
multiplier,
|
|
349
366
|
debug = false,
|
|
350
|
-
onFailedAttempt
|
|
367
|
+
onFailedAttempt,
|
|
368
|
+
maxRetries = Infinity
|
|
351
369
|
} = backoffOptions;
|
|
352
370
|
return (...args) => __async(this, null, function* () {
|
|
353
371
|
var _a;
|
|
@@ -358,7 +376,9 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
358
376
|
while (true) {
|
|
359
377
|
try {
|
|
360
378
|
const result = yield fetchClient(...args);
|
|
361
|
-
if (result.ok)
|
|
379
|
+
if (result.ok) {
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
362
382
|
const err = yield FetchError.fromResponse(result, url.toString());
|
|
363
383
|
throw err;
|
|
364
384
|
} catch (e) {
|
|
@@ -368,12 +388,27 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
368
388
|
} else if (e instanceof FetchError && !HTTP_RETRY_STATUS_CODES.includes(e.status) && e.status >= 400 && e.status < 500) {
|
|
369
389
|
throw e;
|
|
370
390
|
} else {
|
|
371
|
-
|
|
372
|
-
|
|
391
|
+
attempt++;
|
|
392
|
+
if (attempt > maxRetries) {
|
|
393
|
+
if (debug) {
|
|
394
|
+
console.log(
|
|
395
|
+
`Max retries reached (${attempt}/${maxRetries}), giving up`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
throw e;
|
|
399
|
+
}
|
|
400
|
+
const serverMinimumMs = e instanceof FetchError && e.headers ? parseRetryAfterHeader(e.headers[`retry-after`]) : 0;
|
|
401
|
+
const jitter = Math.random() * delay;
|
|
402
|
+
const clientBackoffMs = Math.min(jitter, maxDelay);
|
|
403
|
+
const waitMs = Math.max(serverMinimumMs, clientBackoffMs);
|
|
373
404
|
if (debug) {
|
|
374
|
-
|
|
375
|
-
console.log(
|
|
405
|
+
const source = serverMinimumMs > 0 ? `server+client` : `client`;
|
|
406
|
+
console.log(
|
|
407
|
+
`Retry attempt #${attempt} after ${waitMs}ms (${source}, serverMin=${serverMinimumMs}ms, clientBackoff=${clientBackoffMs}ms)`
|
|
408
|
+
);
|
|
376
409
|
}
|
|
410
|
+
yield new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
411
|
+
delay = Math.min(delay * multiplier, maxDelay);
|
|
377
412
|
}
|
|
378
413
|
}
|
|
379
414
|
}
|
|
@@ -746,9 +781,8 @@ function canonicalShapeKey(url) {
|
|
|
746
781
|
cleanUrl.searchParams.sort();
|
|
747
782
|
return cleanUrl.toString();
|
|
748
783
|
}
|
|
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;
|
|
784
|
+
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, _unsubscribeFromVisibilityChanges, _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
785
|
var ShapeStream = class {
|
|
751
|
-
// Maximum delay cap (ms)
|
|
752
786
|
constructor(options) {
|
|
753
787
|
__privateAdd(this, _ShapeStream_instances);
|
|
754
788
|
__privateAdd(this, _error, null);
|
|
@@ -792,6 +826,8 @@ var ShapeStream = class {
|
|
|
792
826
|
__privateAdd(this, _sseBackoffBaseDelay, 100);
|
|
793
827
|
// Base delay for exponential backoff (ms)
|
|
794
828
|
__privateAdd(this, _sseBackoffMaxDelay, 5e3);
|
|
829
|
+
// Maximum delay cap (ms)
|
|
830
|
+
__privateAdd(this, _unsubscribeFromVisibilityChanges);
|
|
795
831
|
var _a, _b, _c, _d;
|
|
796
832
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
797
833
|
validateOptions(this.options);
|
|
@@ -847,7 +883,9 @@ var ShapeStream = class {
|
|
|
847
883
|
};
|
|
848
884
|
}
|
|
849
885
|
unsubscribeAll() {
|
|
886
|
+
var _a;
|
|
850
887
|
__privateGet(this, _subscribers).clear();
|
|
888
|
+
(_a = __privateGet(this, _unsubscribeFromVisibilityChanges)) == null ? void 0 : _a.call(this);
|
|
851
889
|
}
|
|
852
890
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
853
891
|
lastSyncedAt() {
|
|
@@ -974,6 +1012,7 @@ _maxShortSseConnections = new WeakMap();
|
|
|
974
1012
|
_sseFallbackToLongPolling = new WeakMap();
|
|
975
1013
|
_sseBackoffBaseDelay = new WeakMap();
|
|
976
1014
|
_sseBackoffMaxDelay = new WeakMap();
|
|
1015
|
+
_unsubscribeFromVisibilityChanges = new WeakMap();
|
|
977
1016
|
_ShapeStream_instances = new WeakSet();
|
|
978
1017
|
start_fn = function() {
|
|
979
1018
|
return __async(this, null, function* () {
|
|
@@ -1043,7 +1082,8 @@ requestShape_fn = function() {
|
|
|
1043
1082
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1044
1083
|
}
|
|
1045
1084
|
if (e instanceof FetchBackoffAbortError) {
|
|
1046
|
-
|
|
1085
|
+
const currentState = __privateGet(this, _state);
|
|
1086
|
+
if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM && currentState === `pause-requested`) {
|
|
1047
1087
|
__privateSet(this, _state, `paused`);
|
|
1048
1088
|
}
|
|
1049
1089
|
return;
|
|
@@ -1304,7 +1344,10 @@ pause_fn = function() {
|
|
|
1304
1344
|
}
|
|
1305
1345
|
};
|
|
1306
1346
|
resume_fn = function() {
|
|
1307
|
-
if (__privateGet(this, _started) && __privateGet(this, _state) === `paused`) {
|
|
1347
|
+
if (__privateGet(this, _started) && (__privateGet(this, _state) === `paused` || __privateGet(this, _state) === `pause-requested`)) {
|
|
1348
|
+
if (__privateGet(this, _state) === `pause-requested`) {
|
|
1349
|
+
__privateSet(this, _state, `active`);
|
|
1350
|
+
}
|
|
1308
1351
|
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
1309
1352
|
}
|
|
1310
1353
|
};
|
|
@@ -1376,6 +1419,9 @@ subscribeToVisibilityChanges_fn = function() {
|
|
|
1376
1419
|
}
|
|
1377
1420
|
};
|
|
1378
1421
|
document.addEventListener(`visibilitychange`, visibilityHandler);
|
|
1422
|
+
__privateSet(this, _unsubscribeFromVisibilityChanges, () => {
|
|
1423
|
+
document.removeEventListener(`visibilitychange`, visibilityHandler);
|
|
1424
|
+
});
|
|
1379
1425
|
}
|
|
1380
1426
|
};
|
|
1381
1427
|
/**
|