@electric-sql/client 1.0.14 → 1.1.1
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 +86 -10
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +28 -2
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.legacy-esm.js +86 -10
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +86 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +18 -4
- package/src/constants.ts +4 -0
- package/src/fetch.ts +130 -15
package/dist/cjs/index.cjs
CHANGED
|
@@ -342,6 +342,7 @@ var WHERE_QUERY_PARAM = `where`;
|
|
|
342
342
|
var REPLICA_PARAM = `replica`;
|
|
343
343
|
var WHERE_PARAMS_PARAM = `params`;
|
|
344
344
|
var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
345
|
+
var LIVE_SSE_QUERY_PARAM = `live_sse`;
|
|
345
346
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
346
347
|
var PAUSE_STREAM = `pause-stream`;
|
|
347
348
|
var LOG_MODE_QUERY_PARAM = `log`;
|
|
@@ -368,8 +369,13 @@ var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
|
368
369
|
var HTTP_RETRY_STATUS_CODES = [429];
|
|
369
370
|
var BackoffDefaults = {
|
|
370
371
|
initialDelay: 100,
|
|
371
|
-
maxDelay:
|
|
372
|
-
|
|
372
|
+
maxDelay: 6e4,
|
|
373
|
+
// Cap at 60s - reasonable for long-lived connections
|
|
374
|
+
multiplier: 1.3,
|
|
375
|
+
maxRetries: Infinity,
|
|
376
|
+
// Retry forever - clients may go offline and come back
|
|
377
|
+
retryBudgetPercent: 0.1
|
|
378
|
+
// 10% retry budget prevents amplification
|
|
373
379
|
};
|
|
374
380
|
function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
375
381
|
const {
|
|
@@ -377,8 +383,29 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
377
383
|
maxDelay,
|
|
378
384
|
multiplier,
|
|
379
385
|
debug = false,
|
|
380
|
-
onFailedAttempt
|
|
386
|
+
onFailedAttempt,
|
|
387
|
+
maxRetries = Infinity,
|
|
388
|
+
retryBudgetPercent = 0.1
|
|
381
389
|
} = backoffOptions;
|
|
390
|
+
let totalRequests = 0;
|
|
391
|
+
let totalRetries = 0;
|
|
392
|
+
let budgetResetTime = Date.now() + 6e4;
|
|
393
|
+
function checkRetryBudget(percent) {
|
|
394
|
+
const now = Date.now();
|
|
395
|
+
if (now > budgetResetTime) {
|
|
396
|
+
totalRequests = 0;
|
|
397
|
+
totalRetries = 0;
|
|
398
|
+
budgetResetTime = now + 6e4;
|
|
399
|
+
}
|
|
400
|
+
totalRequests++;
|
|
401
|
+
if (totalRequests < 10) return true;
|
|
402
|
+
const currentRetryRate = totalRetries / totalRequests;
|
|
403
|
+
const hasCapacity = currentRetryRate < percent;
|
|
404
|
+
if (hasCapacity) {
|
|
405
|
+
totalRetries++;
|
|
406
|
+
}
|
|
407
|
+
return hasCapacity;
|
|
408
|
+
}
|
|
382
409
|
return (...args) => __async(this, null, function* () {
|
|
383
410
|
var _a;
|
|
384
411
|
const url = args[0];
|
|
@@ -388,7 +415,10 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
388
415
|
while (true) {
|
|
389
416
|
try {
|
|
390
417
|
const result = yield fetchClient(...args);
|
|
391
|
-
if (result.ok)
|
|
418
|
+
if (result.ok) {
|
|
419
|
+
delay = initialDelay;
|
|
420
|
+
return result;
|
|
421
|
+
}
|
|
392
422
|
const err = yield FetchError.fromResponse(result, url.toString());
|
|
393
423
|
throw err;
|
|
394
424
|
} catch (e) {
|
|
@@ -398,12 +428,51 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
398
428
|
} else if (e instanceof FetchError && !HTTP_RETRY_STATUS_CODES.includes(e.status) && e.status >= 400 && e.status < 500) {
|
|
399
429
|
throw e;
|
|
400
430
|
} else {
|
|
401
|
-
|
|
402
|
-
|
|
431
|
+
attempt++;
|
|
432
|
+
if (attempt >= maxRetries) {
|
|
433
|
+
if (debug) {
|
|
434
|
+
console.log(
|
|
435
|
+
`Max retries reached (${attempt}/${maxRetries}), giving up`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
throw e;
|
|
439
|
+
}
|
|
440
|
+
if (!checkRetryBudget(retryBudgetPercent)) {
|
|
441
|
+
if (debug) {
|
|
442
|
+
console.log(
|
|
443
|
+
`Retry budget exhausted (attempt ${attempt}), backing off`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
yield new Promise((resolve) => setTimeout(resolve, maxDelay));
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
let serverMinimumMs = 0;
|
|
450
|
+
if (e instanceof FetchError && e.headers) {
|
|
451
|
+
const retryAfter = e.headers[`retry-after`];
|
|
452
|
+
if (retryAfter) {
|
|
453
|
+
const retryAfterSec = Number(retryAfter);
|
|
454
|
+
if (Number.isFinite(retryAfterSec) && retryAfterSec > 0) {
|
|
455
|
+
serverMinimumMs = retryAfterSec * 1e3;
|
|
456
|
+
} else {
|
|
457
|
+
const retryDate = Date.parse(retryAfter);
|
|
458
|
+
if (!isNaN(retryDate)) {
|
|
459
|
+
const deltaMs = retryDate - Date.now();
|
|
460
|
+
serverMinimumMs = Math.max(0, Math.min(deltaMs, 36e5));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const jitter = Math.random() * delay;
|
|
466
|
+
const clientBackoffMs = Math.min(jitter, maxDelay);
|
|
467
|
+
const waitMs = Math.max(serverMinimumMs, clientBackoffMs);
|
|
403
468
|
if (debug) {
|
|
404
|
-
|
|
405
|
-
console.log(
|
|
469
|
+
const source = serverMinimumMs > 0 ? `server+client` : `client`;
|
|
470
|
+
console.log(
|
|
471
|
+
`Retry attempt #${attempt} after ${waitMs}ms (${source}, serverMin=${serverMinimumMs}ms, clientBackoff=${clientBackoffMs}ms)`
|
|
472
|
+
);
|
|
406
473
|
}
|
|
474
|
+
yield new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
475
|
+
delay = Math.min(delay * multiplier, maxDelay);
|
|
407
476
|
}
|
|
408
477
|
}
|
|
409
478
|
}
|
|
@@ -412,6 +481,7 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
412
481
|
var NO_BODY_STATUS_CODES = [201, 204, 205];
|
|
413
482
|
function createFetchWithConsumedMessages(fetchClient) {
|
|
414
483
|
return (...args) => __async(this, null, function* () {
|
|
484
|
+
var _a, _b;
|
|
415
485
|
const url = args[0];
|
|
416
486
|
const res = yield fetchClient(...args);
|
|
417
487
|
try {
|
|
@@ -421,6 +491,9 @@ function createFetchWithConsumedMessages(fetchClient) {
|
|
|
421
491
|
const text = yield res.text();
|
|
422
492
|
return new Response(text, res);
|
|
423
493
|
} catch (err) {
|
|
494
|
+
if ((_b = (_a = args[1]) == null ? void 0 : _a.signal) == null ? void 0 : _b.aborted) {
|
|
495
|
+
throw new FetchBackoffAbortError();
|
|
496
|
+
}
|
|
424
497
|
throw new FetchError(
|
|
425
498
|
res.status,
|
|
426
499
|
void 0,
|
|
@@ -1052,7 +1125,7 @@ requestShape_fn = function() {
|
|
|
1052
1125
|
}
|
|
1053
1126
|
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this, _shapeHandle)}-next`;
|
|
1054
1127
|
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
1055
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
1128
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, Array.isArray(e.json) ? e.json : [e.json]);
|
|
1056
1129
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1057
1130
|
} else {
|
|
1058
1131
|
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
@@ -1205,8 +1278,11 @@ onMessages_fn = function(batch, isSseMessage = false) {
|
|
|
1205
1278
|
};
|
|
1206
1279
|
fetchShape_fn = function(opts) {
|
|
1207
1280
|
return __async(this, null, function* () {
|
|
1208
|
-
|
|
1281
|
+
var _a;
|
|
1282
|
+
const useSse = (_a = this.options.liveSse) != null ? _a : this.options.experimentalLiveSse;
|
|
1283
|
+
if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
|
|
1209
1284
|
opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
|
|
1285
|
+
opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`);
|
|
1210
1286
|
return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
|
|
1211
1287
|
}
|
|
1212
1288
|
return __privateMethod(this, _ShapeStream_instances, requestShapeLongPoll_fn).call(this, opts);
|