@electric-sql/client 1.1.2 → 1.1.3

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/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: 1e4,
342
- multiplier: 1.3
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) return result;
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
- yield new Promise((resolve) => setTimeout(resolve, delay));
372
- delay = Math.min(delay * multiplier, maxDelay);
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
- attempt++;
375
- console.log(`Retry attempt #${attempt} after ${delay}ms`);
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
  }