@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/index.legacy-esm.js
CHANGED
|
@@ -467,6 +467,7 @@ var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
|
467
467
|
var LIVE_SSE_QUERY_PARAM = `live_sse`;
|
|
468
468
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
469
469
|
var PAUSE_STREAM = `pause-stream`;
|
|
470
|
+
var SYSTEM_WAKE = `system-wake`;
|
|
470
471
|
var LOG_MODE_QUERY_PARAM = `log`;
|
|
471
472
|
var SUBSET_PARAM_WHERE = `subset__where`;
|
|
472
473
|
var SUBSET_PARAM_LIMIT = `subset__limit`;
|
|
@@ -1142,7 +1143,7 @@ function canonicalShapeKey(url) {
|
|
|
1142
1143
|
cleanUrl.searchParams.sort();
|
|
1143
1144
|
return cleanUrl.toString();
|
|
1144
1145
|
}
|
|
1145
|
-
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;
|
|
1146
|
+
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;
|
|
1146
1147
|
var ShapeStream = class {
|
|
1147
1148
|
constructor(options) {
|
|
1148
1149
|
__privateAdd(this, _ShapeStream_instances);
|
|
@@ -1193,6 +1194,7 @@ var ShapeStream = class {
|
|
|
1193
1194
|
__privateAdd(this, _sseBackoffMaxDelay, 5e3);
|
|
1194
1195
|
// Maximum delay cap (ms)
|
|
1195
1196
|
__privateAdd(this, _unsubscribeFromVisibilityChanges);
|
|
1197
|
+
__privateAdd(this, _unsubscribeFromWakeDetection);
|
|
1196
1198
|
__privateAdd(this, _staleCacheBuster);
|
|
1197
1199
|
// Cache buster set when stale CDN response detected, used on retry requests to bypass cache
|
|
1198
1200
|
__privateAdd(this, _staleCacheRetryCount, 0);
|
|
@@ -1237,6 +1239,7 @@ var ShapeStream = class {
|
|
|
1237
1239
|
));
|
|
1238
1240
|
__privateSet(this, _fetchClient2, createFetchWithConsumedMessages(__privateGet(this, _sseFetchClient)));
|
|
1239
1241
|
__privateMethod(this, _ShapeStream_instances, subscribeToVisibilityChanges_fn).call(this);
|
|
1242
|
+
__privateMethod(this, _ShapeStream_instances, subscribeToWakeDetection_fn).call(this);
|
|
1240
1243
|
}
|
|
1241
1244
|
get shapeHandle() {
|
|
1242
1245
|
return __privateGet(this, _shapeHandle);
|
|
@@ -1263,9 +1266,10 @@ var ShapeStream = class {
|
|
|
1263
1266
|
};
|
|
1264
1267
|
}
|
|
1265
1268
|
unsubscribeAll() {
|
|
1266
|
-
var _a;
|
|
1269
|
+
var _a, _b;
|
|
1267
1270
|
__privateGet(this, _subscribers).clear();
|
|
1268
1271
|
(_a = __privateGet(this, _unsubscribeFromVisibilityChanges)) == null ? void 0 : _a.call(this);
|
|
1272
|
+
(_b = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _b.call(this);
|
|
1269
1273
|
}
|
|
1270
1274
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
1271
1275
|
lastSyncedAt() {
|
|
@@ -1385,7 +1389,21 @@ var ShapeStream = class {
|
|
|
1385
1389
|
fetchUrl = result.fetchUrl;
|
|
1386
1390
|
fetchOptions = { headers: result.requestHeaders };
|
|
1387
1391
|
}
|
|
1388
|
-
const
|
|
1392
|
+
const usedHandle = __privateGet(this, _shapeHandle);
|
|
1393
|
+
let response;
|
|
1394
|
+
try {
|
|
1395
|
+
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
|
|
1396
|
+
} catch (e) {
|
|
1397
|
+
if (e instanceof FetchError && e.status === 409) {
|
|
1398
|
+
if (usedHandle) {
|
|
1399
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1400
|
+
expiredShapesCache.markExpired(shapeKey, usedHandle);
|
|
1401
|
+
}
|
|
1402
|
+
__privateSet(this, _shapeHandle, e.headers[SHAPE_HANDLE_HEADER] || `${usedHandle != null ? usedHandle : `handle`}-next`);
|
|
1403
|
+
return this.fetchSnapshot(opts);
|
|
1404
|
+
}
|
|
1405
|
+
throw e;
|
|
1406
|
+
}
|
|
1389
1407
|
if (!response.ok) {
|
|
1390
1408
|
throw await FetchError.fromResponse(response, fetchUrl.toString());
|
|
1391
1409
|
}
|
|
@@ -1438,6 +1456,7 @@ _sseFallbackToLongPolling = new WeakMap();
|
|
|
1438
1456
|
_sseBackoffBaseDelay = new WeakMap();
|
|
1439
1457
|
_sseBackoffMaxDelay = new WeakMap();
|
|
1440
1458
|
_unsubscribeFromVisibilityChanges = new WeakMap();
|
|
1459
|
+
_unsubscribeFromWakeDetection = new WeakMap();
|
|
1441
1460
|
_staleCacheBuster = new WeakMap();
|
|
1442
1461
|
_staleCacheRetryCount = new WeakMap();
|
|
1443
1462
|
_maxStaleCacheRetries = new WeakMap();
|
|
@@ -1446,7 +1465,7 @@ replayMode_get = function() {
|
|
|
1446
1465
|
return __privateGet(this, _lastSeenCursor) !== void 0;
|
|
1447
1466
|
};
|
|
1448
1467
|
start_fn = async function() {
|
|
1449
|
-
var _a, _b, _c, _d, _e;
|
|
1468
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1450
1469
|
__privateSet(this, _started, true);
|
|
1451
1470
|
try {
|
|
1452
1471
|
await __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
@@ -1472,17 +1491,20 @@ start_fn = async function() {
|
|
|
1472
1491
|
}
|
|
1473
1492
|
__privateSet(this, _connected, false);
|
|
1474
1493
|
(_c = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _c.call(this);
|
|
1494
|
+
(_d = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _d.call(this);
|
|
1475
1495
|
return;
|
|
1476
1496
|
}
|
|
1477
1497
|
if (err instanceof Error) {
|
|
1478
1498
|
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
|
|
1479
1499
|
}
|
|
1480
1500
|
__privateSet(this, _connected, false);
|
|
1481
|
-
(
|
|
1501
|
+
(_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
|
|
1502
|
+
(_f = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _f.call(this);
|
|
1482
1503
|
throw err;
|
|
1483
1504
|
}
|
|
1484
1505
|
__privateSet(this, _connected, false);
|
|
1485
|
-
(
|
|
1506
|
+
(_g = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _g.call(this);
|
|
1507
|
+
(_h = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _h.call(this);
|
|
1486
1508
|
};
|
|
1487
1509
|
requestShape_fn = async function() {
|
|
1488
1510
|
var _a, _b;
|
|
@@ -1507,7 +1529,9 @@ requestShape_fn = async function() {
|
|
|
1507
1529
|
resumingFromPause
|
|
1508
1530
|
});
|
|
1509
1531
|
} catch (e) {
|
|
1510
|
-
|
|
1532
|
+
const abortReason = requestAbortController.signal.reason;
|
|
1533
|
+
const isRestartAbort = requestAbortController.signal.aborted && (abortReason === FORCE_DISCONNECT_AND_REFRESH || abortReason === SYSTEM_WAKE);
|
|
1534
|
+
if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && isRestartAbort) {
|
|
1511
1535
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1512
1536
|
}
|
|
1513
1537
|
if (e instanceof FetchBackoffAbortError) {
|
|
@@ -1714,8 +1738,9 @@ onInitialResponse_fn = async function(response) {
|
|
|
1714
1738
|
);
|
|
1715
1739
|
} else {
|
|
1716
1740
|
console.warn(
|
|
1717
|
-
`[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
|
|
1741
|
+
`[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)}".`
|
|
1718
1742
|
);
|
|
1743
|
+
return;
|
|
1719
1744
|
}
|
|
1720
1745
|
}
|
|
1721
1746
|
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
@@ -1935,8 +1960,11 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
1935
1960
|
errorFn == null ? void 0 : errorFn(error);
|
|
1936
1961
|
});
|
|
1937
1962
|
};
|
|
1963
|
+
hasBrowserVisibilityAPI_fn = function() {
|
|
1964
|
+
return typeof document === `object` && typeof document.hidden === `boolean` && typeof document.addEventListener === `function`;
|
|
1965
|
+
};
|
|
1938
1966
|
subscribeToVisibilityChanges_fn = function() {
|
|
1939
|
-
if (
|
|
1967
|
+
if (__privateMethod(this, _ShapeStream_instances, hasBrowserVisibilityAPI_fn).call(this)) {
|
|
1940
1968
|
const visibilityHandler = () => {
|
|
1941
1969
|
if (document.hidden) {
|
|
1942
1970
|
__privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
|
|
@@ -1950,6 +1978,44 @@ subscribeToVisibilityChanges_fn = function() {
|
|
|
1950
1978
|
});
|
|
1951
1979
|
}
|
|
1952
1980
|
};
|
|
1981
|
+
/**
|
|
1982
|
+
* Detects system wake from sleep using timer gap detection.
|
|
1983
|
+
* When the system sleeps, setInterval timers are paused. On wake,
|
|
1984
|
+
* the elapsed wall-clock time since the last tick will be much larger
|
|
1985
|
+
* than the interval period, indicating the system was asleep.
|
|
1986
|
+
*
|
|
1987
|
+
* Only active in non-browser environments (Bun, Node.js) where
|
|
1988
|
+
* `document.visibilitychange` is not available. In browsers,
|
|
1989
|
+
* `#subscribeToVisibilityChanges` handles this instead. Without wake
|
|
1990
|
+
* detection, in-flight HTTP requests (long-poll or SSE) may hang until
|
|
1991
|
+
* the OS TCP timeout.
|
|
1992
|
+
*/
|
|
1993
|
+
subscribeToWakeDetection_fn = function() {
|
|
1994
|
+
if (__privateMethod(this, _ShapeStream_instances, hasBrowserVisibilityAPI_fn).call(this)) return;
|
|
1995
|
+
const INTERVAL_MS = 2e3;
|
|
1996
|
+
const WAKE_THRESHOLD_MS = 4e3;
|
|
1997
|
+
let lastTickTime = Date.now();
|
|
1998
|
+
const timer = setInterval(() => {
|
|
1999
|
+
const now = Date.now();
|
|
2000
|
+
const elapsed = now - lastTickTime;
|
|
2001
|
+
lastTickTime = now;
|
|
2002
|
+
if (elapsed > INTERVAL_MS + WAKE_THRESHOLD_MS) {
|
|
2003
|
+
if (__privateGet(this, _state) === `active` && __privateGet(this, _requestAbortController)) {
|
|
2004
|
+
__privateSet(this, _isRefreshing, true);
|
|
2005
|
+
__privateGet(this, _requestAbortController).abort(SYSTEM_WAKE);
|
|
2006
|
+
queueMicrotask(() => {
|
|
2007
|
+
__privateSet(this, _isRefreshing, false);
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}, INTERVAL_MS);
|
|
2012
|
+
if (typeof timer === `object` && `unref` in timer) {
|
|
2013
|
+
timer.unref();
|
|
2014
|
+
}
|
|
2015
|
+
__privateSet(this, _unsubscribeFromWakeDetection, () => {
|
|
2016
|
+
clearInterval(timer);
|
|
2017
|
+
});
|
|
2018
|
+
};
|
|
1953
2019
|
/**
|
|
1954
2020
|
* Resets the state of the stream, optionally with a provided
|
|
1955
2021
|
* shape handle
|