@electric-sql/client 1.5.11 → 1.5.13

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.
@@ -678,11 +678,11 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
678
678
  return prefetchClient;
679
679
  }
680
680
  var requiredElectricResponseHeaders = [
681
- `electric-offset`,
682
- `electric-handle`
681
+ CHUNK_LAST_OFFSET_HEADER,
682
+ SHAPE_HANDLE_HEADER
683
683
  ];
684
- var requiredLiveResponseHeaders = [`electric-cursor`];
685
- var requiredNonLiveResponseHeaders = [`electric-schema`];
684
+ var requiredLiveResponseHeaders = [LIVE_CACHE_BUSTER_HEADER];
685
+ var requiredNonLiveResponseHeaders = [SHAPE_SCHEMA_HEADER];
686
686
  function createFetchWithResponseHeadersCheck(fetchClient) {
687
687
  return async (...args) => {
688
688
  const response = await fetchClient(...args);
@@ -1473,18 +1473,15 @@ var ActiveState = class extends ShapeStreamState {
1473
1473
  if (!responseHandle || responseHandle !== expiredHandle) {
1474
1474
  return null;
1475
1475
  }
1476
- if (__privateGet(this, _shared).handle === void 0 || __privateGet(this, _shared).handle === expiredHandle) {
1477
- const retryCount = this.staleCacheRetryCount + 1;
1478
- return {
1479
- action: `stale-retry`,
1480
- state: new StaleRetryState(__spreadProps(__spreadValues({}, this.currentFields), {
1481
- staleCacheBuster: input.createCacheBuster(),
1482
- staleCacheRetryCount: retryCount
1483
- })),
1484
- exceededMaxRetries: retryCount > input.maxStaleCacheRetries
1485
- };
1486
- }
1487
- return { action: `ignored`, state: this };
1476
+ const retryCount = this.staleCacheRetryCount + 1;
1477
+ return {
1478
+ action: `stale-retry`,
1479
+ state: new StaleRetryState(__spreadProps(__spreadValues({}, this.currentFields), {
1480
+ staleCacheBuster: input.createCacheBuster(),
1481
+ staleCacheRetryCount: retryCount
1482
+ })),
1483
+ exceededMaxRetries: retryCount > input.maxStaleCacheRetries
1484
+ };
1488
1485
  }
1489
1486
  // --- handleMessageBatch: template method with onUpToDate override point ---
1490
1487
  handleMessageBatch(input) {
@@ -1983,7 +1980,7 @@ function canonicalShapeKey(url) {
1983
1980
  cleanUrl.searchParams.sort();
1984
1981
  return cleanUrl.toString();
1985
1982
  }
1986
- var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _syncState, _connected, _mode, _onError, _requestAbortController, _refreshCount, _snapshotCounter, _ShapeStream_instances, isRefreshing_get, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _pauseLock, _currentFetchUrl, _lastSseConnectionStartTime, _minSseConnectionDuration, _maxShortSseConnections, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _unsubscribeFromVisibilityChanges, _unsubscribeFromWakeDetection, _maxStaleCacheRetries, _recentRequestEntries, _fastLoopWindowMs, _fastLoopThreshold, _fastLoopBackoffBaseMs, _fastLoopBackoffMaxMs, _fastLoopConsecutiveCount, _fastLoopMaxCount, _refetchCacheBuster, start_fn, teardown_fn, requestShape_fn, checkFastLoop_fn, constructUrl_fn, createAbortListener_fn, onInitialResponse_fn, onMessages_fn, fetchShape_fn, requestShapeLongPoll_fn, requestShapeSSE_fn, nextTick_fn, publish_fn, sendErrorToSubscribers_fn, hasBrowserVisibilityAPI_fn, subscribeToVisibilityChanges_fn, subscribeToWakeDetection_fn, reset_fn, buildSubsetBody_fn;
1983
+ var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _syncState, _connected, _mode, _onError, _requestAbortController, _refreshCount, _snapshotCounter, _ShapeStream_instances, isRefreshing_get, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _pauseLock, _currentFetchUrl, _lastSseConnectionStartTime, _minSseConnectionDuration, _maxShortSseConnections, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _unsubscribeFromVisibilityChanges, _unsubscribeFromWakeDetection, _maxStaleCacheRetries, _recentRequestEntries, _fastLoopWindowMs, _fastLoopThreshold, _fastLoopBackoffBaseMs, _fastLoopBackoffMaxMs, _fastLoopConsecutiveCount, _fastLoopMaxCount, _pendingRequestShapeCacheBuster, _maxSnapshotRetries, start_fn, teardown_fn, requestShape_fn, checkFastLoop_fn, constructUrl_fn, createAbortListener_fn, onInitialResponse_fn, onMessages_fn, fetchShape_fn, requestShapeLongPoll_fn, requestShapeSSE_fn, nextTick_fn, publish_fn, sendErrorToSubscribers_fn, hasBrowserVisibilityAPI_fn, subscribeToVisibilityChanges_fn, subscribeToWakeDetection_fn, reset_fn, fetchSnapshotWithRetry_fn, buildSubsetBody_fn;
1987
1984
  var ShapeStream = class {
1988
1985
  constructor(options) {
1989
1986
  __privateAdd(this, _ShapeStream_instances);
@@ -2030,7 +2027,8 @@ var ShapeStream = class {
2030
2027
  __privateAdd(this, _fastLoopBackoffMaxMs, 5e3);
2031
2028
  __privateAdd(this, _fastLoopConsecutiveCount, 0);
2032
2029
  __privateAdd(this, _fastLoopMaxCount, 5);
2033
- __privateAdd(this, _refetchCacheBuster);
2030
+ __privateAdd(this, _pendingRequestShapeCacheBuster);
2031
+ __privateAdd(this, _maxSnapshotRetries, 5);
2034
2032
  var _a, _b, _c, _d;
2035
2033
  this.options = __spreadValues({ subscribe: true }, options);
2036
2034
  validateOptions(this.options);
@@ -2188,7 +2186,8 @@ var ShapeStream = class {
2188
2186
  __privateGet(this, _pauseLock).acquire(snapshotReason);
2189
2187
  const snapshotWarnTimer = setTimeout(() => {
2190
2188
  console.warn(
2191
- `[Electric] Snapshot "${snapshotReason}" has held the pause lock for 30s \u2014 possible hung request or leaked lock. Current holders: ${[.../* @__PURE__ */ new Set([snapshotReason])].join(`, `)}`
2189
+ `[Electric] Snapshot "${snapshotReason}" has held the pause lock for 30s \u2014 possible hung request or leaked lock. Current holders: ${[.../* @__PURE__ */ new Set([snapshotReason])].join(`, `)}`,
2190
+ new Error(`stack trace`)
2192
2191
  );
2193
2192
  }, 3e4);
2194
2193
  try {
@@ -2217,7 +2216,8 @@ var ShapeStream = class {
2217
2216
  __privateSet(this, _syncState, transition.state);
2218
2217
  } else {
2219
2218
  console.warn(
2220
- `[Electric] Snapshot response metadata was not accepted by state "${__privateGet(this, _syncState).kind}" (action: ${transition.action}). Stream offset was not advanced from snapshot.`
2219
+ `[Electric] Snapshot response metadata was not accepted by state "${__privateGet(this, _syncState).kind}" (action: ${transition.action}). Stream offset was not advanced from snapshot.`,
2220
+ new Error(`stack trace`)
2221
2221
  );
2222
2222
  }
2223
2223
  }
@@ -2242,64 +2242,7 @@ var ShapeStream = class {
2242
2242
  * @returns The metadata, data, and the response's offset/handle for state advancement.
2243
2243
  */
2244
2244
  async fetchSnapshot(opts) {
2245
- var _a, _b, _c;
2246
- const method = (_b = (_a = opts.method) != null ? _a : this.options.subsetMethod) != null ? _b : `GET`;
2247
- const usePost = method === `POST`;
2248
- let fetchUrl;
2249
- let fetchOptions;
2250
- if (usePost) {
2251
- const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true);
2252
- fetchUrl = result.fetchUrl;
2253
- fetchOptions = {
2254
- method: `POST`,
2255
- headers: __spreadProps(__spreadValues({}, result.requestHeaders), {
2256
- "Content-Type": `application/json`
2257
- }),
2258
- body: bigintSafeStringify(__privateMethod(this, _ShapeStream_instances, buildSubsetBody_fn).call(this, opts))
2259
- };
2260
- } else {
2261
- const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
2262
- fetchUrl = result.fetchUrl;
2263
- fetchOptions = { headers: result.requestHeaders };
2264
- }
2265
- const usedHandle = __privateGet(this, _syncState).handle;
2266
- let response;
2267
- try {
2268
- response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
2269
- } catch (e) {
2270
- if (e instanceof FetchError && e.status === 409) {
2271
- if (usedHandle) {
2272
- const shapeKey = canonicalShapeKey(fetchUrl);
2273
- expiredShapesCache.markExpired(shapeKey, usedHandle);
2274
- }
2275
- const nextHandle = e.headers[SHAPE_HANDLE_HEADER];
2276
- if (nextHandle) {
2277
- __privateSet(this, _syncState, __privateGet(this, _syncState).withHandle(nextHandle));
2278
- } else {
2279
- console.warn(
2280
- `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`
2281
- );
2282
- __privateSet(this, _refetchCacheBuster, createCacheBuster());
2283
- }
2284
- return this.fetchSnapshot(opts);
2285
- }
2286
- throw e;
2287
- }
2288
- if (!response.ok) {
2289
- throw await FetchError.fromResponse(response, fetchUrl.toString());
2290
- }
2291
- const schema = (_c = __privateGet(this, _syncState).schema) != null ? _c : getSchemaFromHeaders(response.headers, {
2292
- required: true,
2293
- url: fetchUrl.toString()
2294
- });
2295
- const { metadata, data: rawData } = await response.json();
2296
- const data = __privateGet(this, _messageParser).parseSnapshotData(
2297
- rawData,
2298
- schema
2299
- );
2300
- const responseOffset = response.headers.get(CHUNK_LAST_OFFSET_HEADER) || null;
2301
- const responseHandle = response.headers.get(SHAPE_HANDLE_HEADER);
2302
- return { metadata, data, responseOffset, responseHandle };
2245
+ return __privateMethod(this, _ShapeStream_instances, fetchSnapshotWithRetry_fn).call(this, opts, 0);
2303
2246
  }
2304
2247
  };
2305
2248
  _error = new WeakMap();
@@ -2341,7 +2284,8 @@ _fastLoopBackoffBaseMs = new WeakMap();
2341
2284
  _fastLoopBackoffMaxMs = new WeakMap();
2342
2285
  _fastLoopConsecutiveCount = new WeakMap();
2343
2286
  _fastLoopMaxCount = new WeakMap();
2344
- _refetchCacheBuster = new WeakMap();
2287
+ _pendingRequestShapeCacheBuster = new WeakMap();
2288
+ _maxSnapshotRetries = new WeakMap();
2345
2289
  start_fn = async function() {
2346
2290
  var _a, _b;
2347
2291
  __privateSet(this, _started, true);
@@ -2393,9 +2337,15 @@ teardown_fn = function() {
2393
2337
  (_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
2394
2338
  (_b = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _b.call(this);
2395
2339
  };
2396
- requestShape_fn = async function() {
2340
+ requestShape_fn = async function(requestShapeCacheBuster) {
2397
2341
  var _a, _b;
2398
- if (__privateGet(this, _pauseLock).isPaused) return;
2342
+ const activeCacheBuster = requestShapeCacheBuster != null ? requestShapeCacheBuster : __privateGet(this, _pendingRequestShapeCacheBuster);
2343
+ if (__privateGet(this, _pauseLock).isPaused) {
2344
+ if (activeCacheBuster) {
2345
+ __privateSet(this, _pendingRequestShapeCacheBuster, activeCacheBuster);
2346
+ }
2347
+ return;
2348
+ }
2399
2349
  if (!this.options.subscribe && (((_a = this.options.signal) == null ? void 0 : _a.aborted) || __privateGet(this, _syncState).isUpToDate)) {
2400
2350
  return;
2401
2351
  }
@@ -2412,15 +2362,23 @@ requestShape_fn = async function() {
2412
2362
  }
2413
2363
  const { url, signal } = this.options;
2414
2364
  const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, url, resumingFromPause);
2365
+ if (activeCacheBuster) {
2366
+ fetchUrl.searchParams.set(CACHE_BUSTER_QUERY_PARAM, activeCacheBuster);
2367
+ fetchUrl.searchParams.sort();
2368
+ }
2415
2369
  const abortListener = await __privateMethod(this, _ShapeStream_instances, createAbortListener_fn).call(this, signal);
2416
2370
  const requestAbortController = __privateGet(this, _requestAbortController);
2417
2371
  if (__privateGet(this, _pauseLock).isPaused) {
2418
2372
  if (abortListener && signal) {
2419
2373
  signal.removeEventListener(`abort`, abortListener);
2420
2374
  }
2375
+ if (activeCacheBuster) {
2376
+ __privateSet(this, _pendingRequestShapeCacheBuster, activeCacheBuster);
2377
+ }
2421
2378
  __privateSet(this, _requestAbortController, void 0);
2422
2379
  return;
2423
2380
  }
2381
+ __privateSet(this, _pendingRequestShapeCacheBuster, void 0);
2424
2382
  try {
2425
2383
  await __privateMethod(this, _ShapeStream_instances, fetchShape_fn).call(this, {
2426
2384
  fetchUrl,
@@ -2447,16 +2405,18 @@ requestShape_fn = async function() {
2447
2405
  expiredShapesCache.markExpired(shapeKey, __privateGet(this, _syncState).handle);
2448
2406
  }
2449
2407
  const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
2408
+ let nextRequestShapeCacheBuster;
2450
2409
  if (!newShapeHandle) {
2451
2410
  console.warn(
2452
- `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`
2411
+ `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`,
2412
+ new Error(`stack trace`)
2453
2413
  );
2454
- __privateSet(this, _refetchCacheBuster, createCacheBuster());
2414
+ nextRequestShapeCacheBuster = createCacheBuster();
2455
2415
  }
2456
2416
  __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
2457
2417
  const messages409 = Array.isArray(e.json) ? e.json : e.json != null ? [e.json] : [];
2458
2418
  await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messages409);
2459
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
2419
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this, nextRequestShapeCacheBuster);
2460
2420
  } else {
2461
2421
  throw e;
2462
2422
  }
@@ -2497,7 +2457,8 @@ For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`
2497
2457
  }
2498
2458
  if (__privateGet(this, _fastLoopConsecutiveCount) === 1) {
2499
2459
  console.warn(
2500
- `[Electric] Detected fast retry loop (${__privateGet(this, _fastLoopThreshold)} requests in ${__privateGet(this, _fastLoopWindowMs)}ms at the same offset). Clearing client-side caches and resetting stream to recover. If this persists, check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key, and that required Electric headers are forwarded to the client. For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`
2460
+ `[Electric] Detected fast retry loop (${__privateGet(this, _fastLoopThreshold)} requests in ${__privateGet(this, _fastLoopWindowMs)}ms at the same offset). Clearing client-side caches and resetting stream to recover. If this persists, check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key, and that required Electric headers are forwarded to the client. For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`,
2461
+ new Error(`stack trace`)
2501
2462
  );
2502
2463
  if (__privateGet(this, _currentFetchUrl)) {
2503
2464
  const shapeKey = canonicalShapeKey(__privateGet(this, _currentFetchUrl));
@@ -2621,13 +2582,6 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
2621
2582
  if (expiredHandle) {
2622
2583
  fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle);
2623
2584
  }
2624
- if (__privateGet(this, _refetchCacheBuster)) {
2625
- fetchUrl.searchParams.set(
2626
- CACHE_BUSTER_QUERY_PARAM,
2627
- __privateGet(this, _refetchCacheBuster)
2628
- );
2629
- __privateSet(this, _refetchCacheBuster, void 0);
2630
- }
2631
2585
  fetchUrl.searchParams.sort();
2632
2586
  return {
2633
2587
  fetchUrl,
@@ -2680,7 +2634,8 @@ onInitialResponse_fn = async function(response) {
2680
2634
  );
2681
2635
  }
2682
2636
  console.warn(
2683
- `[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. For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL} Retrying with a random cache buster to bypass the stale cache (attempt ${__privateGet(this, _syncState).staleCacheRetryCount}/${__privateGet(this, _maxStaleCacheRetries)}).`
2637
+ `[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. For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL} Retrying with a random cache buster to bypass the stale cache (attempt ${__privateGet(this, _syncState).staleCacheRetryCount}/${__privateGet(this, _maxStaleCacheRetries)}).`,
2638
+ new Error(`stack trace`)
2684
2639
  );
2685
2640
  throw new StaleCacheError(
2686
2641
  `Received stale cached response with expired handle "${shapeHandle}". This indicates a proxy/CDN caching misconfiguration. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key.`
@@ -2688,7 +2643,8 @@ onInitialResponse_fn = async function(response) {
2688
2643
  }
2689
2644
  if (transition.action === `ignored`) {
2690
2645
  console.warn(
2691
- `[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, _syncState).handle}".`
2646
+ `[Electric] Response was ignored by state "${__privateGet(this, _syncState).kind}". The response body will be skipped. This may indicate a proxy/CDN caching issue or a client state machine bug.`,
2647
+ new Error(`stack trace`)
2692
2648
  );
2693
2649
  return false;
2694
2650
  }
@@ -2697,7 +2653,8 @@ onInitialResponse_fn = async function(response) {
2697
2653
  onMessages_fn = async function(batch, isSseMessage = false) {
2698
2654
  if (!Array.isArray(batch)) {
2699
2655
  console.warn(
2700
- `[Electric] #onMessages called with non-array argument (${typeof batch}). This is a client bug \u2014 please report it.`
2656
+ `[Electric] #onMessages called with non-array argument (${typeof batch}). This is a client bug \u2014 please report it.`,
2657
+ new Error(`stack trace`)
2701
2658
  );
2702
2659
  return;
2703
2660
  }
@@ -2844,7 +2801,8 @@ requestShapeSSE_fn = async function(opts) {
2844
2801
  __privateSet(this, _syncState, transition.state);
2845
2802
  if (transition.fellBackToLongPolling) {
2846
2803
  console.warn(
2847
- `[Electric] SSE connections are closing immediately (possibly due to proxy buffering or misconfiguration). Falling back to long polling. Your proxy must support streaming SSE responses (not buffer the complete response). Configuration: Nginx add 'X-Accel-Buffering: no', Caddy add 'flush_interval -1' to reverse_proxy. Note: Do NOT disable caching entirely - Electric uses cache headers to enable request collapsing for efficiency.`
2804
+ `[Electric] SSE connections are closing immediately (possibly due to proxy buffering or misconfiguration). Falling back to long polling. Your proxy must support streaming SSE responses (not buffer the complete response). Configuration: Nginx add 'X-Accel-Buffering: no', Caddy add 'flush_interval -1' to reverse_proxy. Note: Do NOT disable caching entirely - Electric uses cache headers to enable request collapsing for efficiency.`,
2805
+ new Error(`stack trace`)
2848
2806
  );
2849
2807
  } else if (transition.wasShortConnection) {
2850
2808
  const maxDelay = Math.min(
@@ -2965,6 +2923,86 @@ reset_fn = function(handle) {
2965
2923
  __privateSet(this, _connected, false);
2966
2924
  __privateGet(this, _pauseLock).releaseAllMatching(`snapshot`);
2967
2925
  };
2926
+ fetchSnapshotWithRetry_fn = async function(opts, retryCount, cacheBuster) {
2927
+ var _a, _b, _c;
2928
+ const method = (_b = (_a = opts.method) != null ? _a : this.options.subsetMethod) != null ? _b : `GET`;
2929
+ const usePost = method === `POST`;
2930
+ let fetchUrl;
2931
+ let fetchOptions;
2932
+ if (usePost) {
2933
+ const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true);
2934
+ fetchUrl = result.fetchUrl;
2935
+ fetchOptions = {
2936
+ method: `POST`,
2937
+ headers: __spreadProps(__spreadValues({}, result.requestHeaders), {
2938
+ "Content-Type": `application/json`
2939
+ }),
2940
+ body: bigintSafeStringify(__privateMethod(this, _ShapeStream_instances, buildSubsetBody_fn).call(this, opts))
2941
+ };
2942
+ } else {
2943
+ const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
2944
+ fetchUrl = result.fetchUrl;
2945
+ fetchOptions = { headers: result.requestHeaders };
2946
+ }
2947
+ if (cacheBuster) {
2948
+ fetchUrl.searchParams.set(CACHE_BUSTER_QUERY_PARAM, cacheBuster);
2949
+ fetchUrl.searchParams.sort();
2950
+ }
2951
+ const usedHandle = __privateGet(this, _syncState).handle;
2952
+ let response;
2953
+ try {
2954
+ response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
2955
+ } catch (e) {
2956
+ if (e instanceof FetchError && e.status === 409) {
2957
+ const nextRetryCount = retryCount + 1;
2958
+ if (nextRetryCount > __privateGet(this, _maxSnapshotRetries)) {
2959
+ throw new FetchError(
2960
+ 502,
2961
+ void 0,
2962
+ void 0,
2963
+ {},
2964
+ fetchUrl.toString(),
2965
+ `Snapshot request stuck in 409 retry loop after ${__privateGet(this, _maxSnapshotRetries)} attempts. This indicates a proxy/CDN misconfiguration. For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`
2966
+ );
2967
+ }
2968
+ if (usedHandle) {
2969
+ const shapeKey = canonicalShapeKey(fetchUrl);
2970
+ expiredShapesCache.markExpired(shapeKey, usedHandle);
2971
+ }
2972
+ const nextHandle = e.headers[SHAPE_HANDLE_HEADER];
2973
+ let nextCacheBuster;
2974
+ if (nextHandle) {
2975
+ __privateSet(this, _syncState, __privateGet(this, _syncState).withHandle(nextHandle));
2976
+ if (nextHandle === usedHandle) {
2977
+ nextCacheBuster = createCacheBuster();
2978
+ }
2979
+ } else {
2980
+ console.warn(
2981
+ `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`,
2982
+ new Error(`stack trace`)
2983
+ );
2984
+ nextCacheBuster = createCacheBuster();
2985
+ }
2986
+ return __privateMethod(this, _ShapeStream_instances, fetchSnapshotWithRetry_fn).call(this, opts, nextRetryCount, nextCacheBuster);
2987
+ }
2988
+ throw e;
2989
+ }
2990
+ if (!response.ok) {
2991
+ throw await FetchError.fromResponse(response, fetchUrl.toString());
2992
+ }
2993
+ const schema = (_c = __privateGet(this, _syncState).schema) != null ? _c : getSchemaFromHeaders(response.headers, {
2994
+ required: true,
2995
+ url: fetchUrl.toString()
2996
+ });
2997
+ const { metadata, data: rawData } = await response.json();
2998
+ const data = __privateGet(this, _messageParser).parseSnapshotData(
2999
+ rawData,
3000
+ schema
3001
+ );
3002
+ const responseOffset = response.headers.get(CHUNK_LAST_OFFSET_HEADER) || null;
3003
+ const responseHandle = response.headers.get(SHAPE_HANDLE_HEADER);
3004
+ return { metadata, data, responseOffset, responseHandle };
3005
+ };
2968
3006
  buildSubsetBody_fn = function(opts) {
2969
3007
  var _a, _b, _c, _d;
2970
3008
  const body = {};