@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/index.mjs
CHANGED
|
@@ -311,6 +311,7 @@ var WHERE_QUERY_PARAM = `where`;
|
|
|
311
311
|
var REPLICA_PARAM = `replica`;
|
|
312
312
|
var WHERE_PARAMS_PARAM = `params`;
|
|
313
313
|
var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
314
|
+
var LIVE_SSE_QUERY_PARAM = `live_sse`;
|
|
314
315
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
315
316
|
var PAUSE_STREAM = `pause-stream`;
|
|
316
317
|
var LOG_MODE_QUERY_PARAM = `log`;
|
|
@@ -337,8 +338,13 @@ var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
|
337
338
|
var HTTP_RETRY_STATUS_CODES = [429];
|
|
338
339
|
var BackoffDefaults = {
|
|
339
340
|
initialDelay: 100,
|
|
340
|
-
maxDelay:
|
|
341
|
-
|
|
341
|
+
maxDelay: 6e4,
|
|
342
|
+
// Cap at 60s - reasonable for long-lived connections
|
|
343
|
+
multiplier: 1.3,
|
|
344
|
+
maxRetries: Infinity,
|
|
345
|
+
// Retry forever - clients may go offline and come back
|
|
346
|
+
retryBudgetPercent: 0.1
|
|
347
|
+
// 10% retry budget prevents amplification
|
|
342
348
|
};
|
|
343
349
|
function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
344
350
|
const {
|
|
@@ -346,8 +352,29 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
346
352
|
maxDelay,
|
|
347
353
|
multiplier,
|
|
348
354
|
debug = false,
|
|
349
|
-
onFailedAttempt
|
|
355
|
+
onFailedAttempt,
|
|
356
|
+
maxRetries = Infinity,
|
|
357
|
+
retryBudgetPercent = 0.1
|
|
350
358
|
} = backoffOptions;
|
|
359
|
+
let totalRequests = 0;
|
|
360
|
+
let totalRetries = 0;
|
|
361
|
+
let budgetResetTime = Date.now() + 6e4;
|
|
362
|
+
function checkRetryBudget(percent) {
|
|
363
|
+
const now = Date.now();
|
|
364
|
+
if (now > budgetResetTime) {
|
|
365
|
+
totalRequests = 0;
|
|
366
|
+
totalRetries = 0;
|
|
367
|
+
budgetResetTime = now + 6e4;
|
|
368
|
+
}
|
|
369
|
+
totalRequests++;
|
|
370
|
+
if (totalRequests < 10) return true;
|
|
371
|
+
const currentRetryRate = totalRetries / totalRequests;
|
|
372
|
+
const hasCapacity = currentRetryRate < percent;
|
|
373
|
+
if (hasCapacity) {
|
|
374
|
+
totalRetries++;
|
|
375
|
+
}
|
|
376
|
+
return hasCapacity;
|
|
377
|
+
}
|
|
351
378
|
return (...args) => __async(this, null, function* () {
|
|
352
379
|
var _a;
|
|
353
380
|
const url = args[0];
|
|
@@ -357,7 +384,10 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
357
384
|
while (true) {
|
|
358
385
|
try {
|
|
359
386
|
const result = yield fetchClient(...args);
|
|
360
|
-
if (result.ok)
|
|
387
|
+
if (result.ok) {
|
|
388
|
+
delay = initialDelay;
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
361
391
|
const err = yield FetchError.fromResponse(result, url.toString());
|
|
362
392
|
throw err;
|
|
363
393
|
} catch (e) {
|
|
@@ -367,12 +397,51 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
367
397
|
} else if (e instanceof FetchError && !HTTP_RETRY_STATUS_CODES.includes(e.status) && e.status >= 400 && e.status < 500) {
|
|
368
398
|
throw e;
|
|
369
399
|
} else {
|
|
370
|
-
|
|
371
|
-
|
|
400
|
+
attempt++;
|
|
401
|
+
if (attempt >= maxRetries) {
|
|
402
|
+
if (debug) {
|
|
403
|
+
console.log(
|
|
404
|
+
`Max retries reached (${attempt}/${maxRetries}), giving up`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
throw e;
|
|
408
|
+
}
|
|
409
|
+
if (!checkRetryBudget(retryBudgetPercent)) {
|
|
410
|
+
if (debug) {
|
|
411
|
+
console.log(
|
|
412
|
+
`Retry budget exhausted (attempt ${attempt}), backing off`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
yield new Promise((resolve) => setTimeout(resolve, maxDelay));
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
let serverMinimumMs = 0;
|
|
419
|
+
if (e instanceof FetchError && e.headers) {
|
|
420
|
+
const retryAfter = e.headers[`retry-after`];
|
|
421
|
+
if (retryAfter) {
|
|
422
|
+
const retryAfterSec = Number(retryAfter);
|
|
423
|
+
if (Number.isFinite(retryAfterSec) && retryAfterSec > 0) {
|
|
424
|
+
serverMinimumMs = retryAfterSec * 1e3;
|
|
425
|
+
} else {
|
|
426
|
+
const retryDate = Date.parse(retryAfter);
|
|
427
|
+
if (!isNaN(retryDate)) {
|
|
428
|
+
const deltaMs = retryDate - Date.now();
|
|
429
|
+
serverMinimumMs = Math.max(0, Math.min(deltaMs, 36e5));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const jitter = Math.random() * delay;
|
|
435
|
+
const clientBackoffMs = Math.min(jitter, maxDelay);
|
|
436
|
+
const waitMs = Math.max(serverMinimumMs, clientBackoffMs);
|
|
372
437
|
if (debug) {
|
|
373
|
-
|
|
374
|
-
console.log(
|
|
438
|
+
const source = serverMinimumMs > 0 ? `server+client` : `client`;
|
|
439
|
+
console.log(
|
|
440
|
+
`Retry attempt #${attempt} after ${waitMs}ms (${source}, serverMin=${serverMinimumMs}ms, clientBackoff=${clientBackoffMs}ms)`
|
|
441
|
+
);
|
|
375
442
|
}
|
|
443
|
+
yield new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
444
|
+
delay = Math.min(delay * multiplier, maxDelay);
|
|
376
445
|
}
|
|
377
446
|
}
|
|
378
447
|
}
|
|
@@ -381,6 +450,7 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
|
|
|
381
450
|
var NO_BODY_STATUS_CODES = [201, 204, 205];
|
|
382
451
|
function createFetchWithConsumedMessages(fetchClient) {
|
|
383
452
|
return (...args) => __async(this, null, function* () {
|
|
453
|
+
var _a, _b;
|
|
384
454
|
const url = args[0];
|
|
385
455
|
const res = yield fetchClient(...args);
|
|
386
456
|
try {
|
|
@@ -390,6 +460,9 @@ function createFetchWithConsumedMessages(fetchClient) {
|
|
|
390
460
|
const text = yield res.text();
|
|
391
461
|
return new Response(text, res);
|
|
392
462
|
} catch (err) {
|
|
463
|
+
if ((_b = (_a = args[1]) == null ? void 0 : _a.signal) == null ? void 0 : _b.aborted) {
|
|
464
|
+
throw new FetchBackoffAbortError();
|
|
465
|
+
}
|
|
393
466
|
throw new FetchError(
|
|
394
467
|
res.status,
|
|
395
468
|
void 0,
|
|
@@ -1023,7 +1096,7 @@ requestShape_fn = function() {
|
|
|
1023
1096
|
}
|
|
1024
1097
|
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this, _shapeHandle)}-next`;
|
|
1025
1098
|
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
1026
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
1099
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, Array.isArray(e.json) ? e.json : [e.json]);
|
|
1027
1100
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1028
1101
|
} else {
|
|
1029
1102
|
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
@@ -1176,8 +1249,11 @@ onMessages_fn = function(batch, isSseMessage = false) {
|
|
|
1176
1249
|
};
|
|
1177
1250
|
fetchShape_fn = function(opts) {
|
|
1178
1251
|
return __async(this, null, function* () {
|
|
1179
|
-
|
|
1252
|
+
var _a;
|
|
1253
|
+
const useSse = (_a = this.options.liveSse) != null ? _a : this.options.experimentalLiveSse;
|
|
1254
|
+
if (__privateGet(this, _isUpToDate) && useSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
|
|
1180
1255
|
opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
|
|
1256
|
+
opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`);
|
|
1181
1257
|
return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
|
|
1182
1258
|
}
|
|
1183
1259
|
return __privateMethod(this, _ShapeStream_instances, requestShapeLongPoll_fn).call(this, opts);
|