@electric-sql/client 1.5.0 → 1.5.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/cjs/index.cjs +75 -9
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.legacy-esm.js +75 -9
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +75 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +96 -9
- package/src/constants.ts +1 -0
package/dist/cjs/index.cjs
CHANGED
|
@@ -504,6 +504,7 @@ var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
|
504
504
|
var LIVE_SSE_QUERY_PARAM = `live_sse`;
|
|
505
505
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
506
506
|
var PAUSE_STREAM = `pause-stream`;
|
|
507
|
+
var SYSTEM_WAKE = `system-wake`;
|
|
507
508
|
var LOG_MODE_QUERY_PARAM = `log`;
|
|
508
509
|
var SUBSET_PARAM_WHERE = `subset__where`;
|
|
509
510
|
var SUBSET_PARAM_LIMIT = `subset__limit`;
|
|
@@ -1177,7 +1178,7 @@ function canonicalShapeKey(url) {
|
|
|
1177
1178
|
cleanUrl.searchParams.sort();
|
|
1178
1179
|
return cleanUrl.toString();
|
|
1179
1180
|
}
|
|
1180
|
-
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, _lastSeenCursor, _currentFetchUrl, _lastSseConnectionStartTime, _minSseConnectionDuration, _consecutiveShortSseConnections, _maxShortSseConnections, _sseFallbackToLongPolling, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _unsubscribeFromVisibilityChanges, _staleCacheBuster, _staleCacheRetryCount, _maxStaleCacheRetries, _ShapeStream_instances, replayMode_get, 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, buildSubsetBody_fn;
|
|
1181
|
+
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, _lastSeenCursor, _currentFetchUrl, _lastSseConnectionStartTime, _minSseConnectionDuration, _consecutiveShortSseConnections, _maxShortSseConnections, _sseFallbackToLongPolling, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _unsubscribeFromVisibilityChanges, _unsubscribeFromWakeDetection, _staleCacheBuster, _staleCacheRetryCount, _maxStaleCacheRetries, _ShapeStream_instances, replayMode_get, 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, hasBrowserVisibilityAPI_fn, subscribeToVisibilityChanges_fn, subscribeToWakeDetection_fn, reset_fn, buildSubsetBody_fn;
|
|
1181
1182
|
var ShapeStream = class {
|
|
1182
1183
|
constructor(options) {
|
|
1183
1184
|
__privateAdd(this, _ShapeStream_instances);
|
|
@@ -1228,6 +1229,7 @@ var ShapeStream = class {
|
|
|
1228
1229
|
__privateAdd(this, _sseBackoffMaxDelay, 5e3);
|
|
1229
1230
|
// Maximum delay cap (ms)
|
|
1230
1231
|
__privateAdd(this, _unsubscribeFromVisibilityChanges);
|
|
1232
|
+
__privateAdd(this, _unsubscribeFromWakeDetection);
|
|
1231
1233
|
__privateAdd(this, _staleCacheBuster);
|
|
1232
1234
|
// Cache buster set when stale CDN response detected, used on retry requests to bypass cache
|
|
1233
1235
|
__privateAdd(this, _staleCacheRetryCount, 0);
|
|
@@ -1272,6 +1274,7 @@ var ShapeStream = class {
|
|
|
1272
1274
|
));
|
|
1273
1275
|
__privateSet(this, _fetchClient2, createFetchWithConsumedMessages(__privateGet(this, _sseFetchClient)));
|
|
1274
1276
|
__privateMethod(this, _ShapeStream_instances, subscribeToVisibilityChanges_fn).call(this);
|
|
1277
|
+
__privateMethod(this, _ShapeStream_instances, subscribeToWakeDetection_fn).call(this);
|
|
1275
1278
|
}
|
|
1276
1279
|
get shapeHandle() {
|
|
1277
1280
|
return __privateGet(this, _shapeHandle);
|
|
@@ -1298,9 +1301,10 @@ var ShapeStream = class {
|
|
|
1298
1301
|
};
|
|
1299
1302
|
}
|
|
1300
1303
|
unsubscribeAll() {
|
|
1301
|
-
var _a;
|
|
1304
|
+
var _a, _b;
|
|
1302
1305
|
__privateGet(this, _subscribers).clear();
|
|
1303
1306
|
(_a = __privateGet(this, _unsubscribeFromVisibilityChanges)) == null ? void 0 : _a.call(this);
|
|
1307
|
+
(_b = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _b.call(this);
|
|
1304
1308
|
}
|
|
1305
1309
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
1306
1310
|
lastSyncedAt() {
|
|
@@ -1420,7 +1424,21 @@ var ShapeStream = class {
|
|
|
1420
1424
|
fetchUrl = result.fetchUrl;
|
|
1421
1425
|
fetchOptions = { headers: result.requestHeaders };
|
|
1422
1426
|
}
|
|
1423
|
-
const
|
|
1427
|
+
const usedHandle = __privateGet(this, _shapeHandle);
|
|
1428
|
+
let response;
|
|
1429
|
+
try {
|
|
1430
|
+
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
|
|
1431
|
+
} catch (e) {
|
|
1432
|
+
if (e instanceof FetchError && e.status === 409) {
|
|
1433
|
+
if (usedHandle) {
|
|
1434
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1435
|
+
expiredShapesCache.markExpired(shapeKey, usedHandle);
|
|
1436
|
+
}
|
|
1437
|
+
__privateSet(this, _shapeHandle, e.headers[SHAPE_HANDLE_HEADER] || `${usedHandle != null ? usedHandle : `handle`}-next`);
|
|
1438
|
+
return this.fetchSnapshot(opts);
|
|
1439
|
+
}
|
|
1440
|
+
throw e;
|
|
1441
|
+
}
|
|
1424
1442
|
if (!response.ok) {
|
|
1425
1443
|
throw await FetchError.fromResponse(response, fetchUrl.toString());
|
|
1426
1444
|
}
|
|
@@ -1473,6 +1491,7 @@ _sseFallbackToLongPolling = new WeakMap();
|
|
|
1473
1491
|
_sseBackoffBaseDelay = new WeakMap();
|
|
1474
1492
|
_sseBackoffMaxDelay = new WeakMap();
|
|
1475
1493
|
_unsubscribeFromVisibilityChanges = new WeakMap();
|
|
1494
|
+
_unsubscribeFromWakeDetection = new WeakMap();
|
|
1476
1495
|
_staleCacheBuster = new WeakMap();
|
|
1477
1496
|
_staleCacheRetryCount = new WeakMap();
|
|
1478
1497
|
_maxStaleCacheRetries = new WeakMap();
|
|
@@ -1481,7 +1500,7 @@ replayMode_get = function() {
|
|
|
1481
1500
|
return __privateGet(this, _lastSeenCursor) !== void 0;
|
|
1482
1501
|
};
|
|
1483
1502
|
start_fn = async function() {
|
|
1484
|
-
var _a, _b, _c, _d, _e;
|
|
1503
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1485
1504
|
__privateSet(this, _started, true);
|
|
1486
1505
|
try {
|
|
1487
1506
|
await __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
@@ -1507,17 +1526,20 @@ start_fn = async function() {
|
|
|
1507
1526
|
}
|
|
1508
1527
|
__privateSet(this, _connected, false);
|
|
1509
1528
|
(_c = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _c.call(this);
|
|
1529
|
+
(_d = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _d.call(this);
|
|
1510
1530
|
return;
|
|
1511
1531
|
}
|
|
1512
1532
|
if (err instanceof Error) {
|
|
1513
1533
|
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
|
|
1514
1534
|
}
|
|
1515
1535
|
__privateSet(this, _connected, false);
|
|
1516
|
-
(
|
|
1536
|
+
(_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
|
|
1537
|
+
(_f = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _f.call(this);
|
|
1517
1538
|
throw err;
|
|
1518
1539
|
}
|
|
1519
1540
|
__privateSet(this, _connected, false);
|
|
1520
|
-
(
|
|
1541
|
+
(_g = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _g.call(this);
|
|
1542
|
+
(_h = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _h.call(this);
|
|
1521
1543
|
};
|
|
1522
1544
|
requestShape_fn = async function() {
|
|
1523
1545
|
var _a, _b;
|
|
@@ -1542,7 +1564,9 @@ requestShape_fn = async function() {
|
|
|
1542
1564
|
resumingFromPause
|
|
1543
1565
|
});
|
|
1544
1566
|
} catch (e) {
|
|
1545
|
-
|
|
1567
|
+
const abortReason = requestAbortController.signal.reason;
|
|
1568
|
+
const isRestartAbort = requestAbortController.signal.aborted && (abortReason === FORCE_DISCONNECT_AND_REFRESH || abortReason === SYSTEM_WAKE);
|
|
1569
|
+
if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && isRestartAbort) {
|
|
1546
1570
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1547
1571
|
}
|
|
1548
1572
|
if (e instanceof FetchBackoffAbortError) {
|
|
@@ -1749,8 +1773,9 @@ onInitialResponse_fn = async function(response) {
|
|
|
1749
1773
|
);
|
|
1750
1774
|
} else {
|
|
1751
1775
|
console.warn(
|
|
1752
|
-
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. Ignoring the stale
|
|
1776
|
+
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. Ignoring the stale response and continuing with handle "${__privateGet(this, _shapeHandle)}".`
|
|
1753
1777
|
);
|
|
1778
|
+
return;
|
|
1754
1779
|
}
|
|
1755
1780
|
}
|
|
1756
1781
|
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
@@ -1970,8 +1995,11 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
1970
1995
|
errorFn == null ? void 0 : errorFn(error);
|
|
1971
1996
|
});
|
|
1972
1997
|
};
|
|
1998
|
+
hasBrowserVisibilityAPI_fn = function() {
|
|
1999
|
+
return typeof document === `object` && typeof document.hidden === `boolean` && typeof document.addEventListener === `function`;
|
|
2000
|
+
};
|
|
1973
2001
|
subscribeToVisibilityChanges_fn = function() {
|
|
1974
|
-
if (
|
|
2002
|
+
if (__privateMethod(this, _ShapeStream_instances, hasBrowserVisibilityAPI_fn).call(this)) {
|
|
1975
2003
|
const visibilityHandler = () => {
|
|
1976
2004
|
if (document.hidden) {
|
|
1977
2005
|
__privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
|
|
@@ -1985,6 +2013,44 @@ subscribeToVisibilityChanges_fn = function() {
|
|
|
1985
2013
|
});
|
|
1986
2014
|
}
|
|
1987
2015
|
};
|
|
2016
|
+
/**
|
|
2017
|
+
* Detects system wake from sleep using timer gap detection.
|
|
2018
|
+
* When the system sleeps, setInterval timers are paused. On wake,
|
|
2019
|
+
* the elapsed wall-clock time since the last tick will be much larger
|
|
2020
|
+
* than the interval period, indicating the system was asleep.
|
|
2021
|
+
*
|
|
2022
|
+
* Only active in non-browser environments (Bun, Node.js) where
|
|
2023
|
+
* `document.visibilitychange` is not available. In browsers,
|
|
2024
|
+
* `#subscribeToVisibilityChanges` handles this instead. Without wake
|
|
2025
|
+
* detection, in-flight HTTP requests (long-poll or SSE) may hang until
|
|
2026
|
+
* the OS TCP timeout.
|
|
2027
|
+
*/
|
|
2028
|
+
subscribeToWakeDetection_fn = function() {
|
|
2029
|
+
if (__privateMethod(this, _ShapeStream_instances, hasBrowserVisibilityAPI_fn).call(this)) return;
|
|
2030
|
+
const INTERVAL_MS = 2e3;
|
|
2031
|
+
const WAKE_THRESHOLD_MS = 4e3;
|
|
2032
|
+
let lastTickTime = Date.now();
|
|
2033
|
+
const timer = setInterval(() => {
|
|
2034
|
+
const now = Date.now();
|
|
2035
|
+
const elapsed = now - lastTickTime;
|
|
2036
|
+
lastTickTime = now;
|
|
2037
|
+
if (elapsed > INTERVAL_MS + WAKE_THRESHOLD_MS) {
|
|
2038
|
+
if (__privateGet(this, _state) === `active` && __privateGet(this, _requestAbortController)) {
|
|
2039
|
+
__privateSet(this, _isRefreshing, true);
|
|
2040
|
+
__privateGet(this, _requestAbortController).abort(SYSTEM_WAKE);
|
|
2041
|
+
queueMicrotask(() => {
|
|
2042
|
+
__privateSet(this, _isRefreshing, false);
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}, INTERVAL_MS);
|
|
2047
|
+
if (typeof timer === `object` && `unref` in timer) {
|
|
2048
|
+
timer.unref();
|
|
2049
|
+
}
|
|
2050
|
+
__privateSet(this, _unsubscribeFromWakeDetection, () => {
|
|
2051
|
+
clearInterval(timer);
|
|
2052
|
+
});
|
|
2053
|
+
};
|
|
1988
2054
|
/**
|
|
1989
2055
|
* Resets the state of the stream, optionally with a provided
|
|
1990
2056
|
* shape handle
|