@electric-sql/client 1.5.12 → 1.5.14

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
@@ -641,11 +641,11 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
641
641
  return prefetchClient;
642
642
  }
643
643
  var requiredElectricResponseHeaders = [
644
- `electric-offset`,
645
- `electric-handle`
644
+ CHUNK_LAST_OFFSET_HEADER,
645
+ SHAPE_HANDLE_HEADER
646
646
  ];
647
- var requiredLiveResponseHeaders = [`electric-cursor`];
648
- var requiredNonLiveResponseHeaders = [`electric-schema`];
647
+ var requiredLiveResponseHeaders = [LIVE_CACHE_BUSTER_HEADER];
648
+ var requiredNonLiveResponseHeaders = [SHAPE_SCHEMA_HEADER];
649
649
  function createFetchWithResponseHeadersCheck(fetchClient) {
650
650
  return async (...args) => {
651
651
  const response = await fetchClient(...args);
@@ -1436,18 +1436,15 @@ var ActiveState = class extends ShapeStreamState {
1436
1436
  if (!responseHandle || responseHandle !== expiredHandle) {
1437
1437
  return null;
1438
1438
  }
1439
- if (__privateGet(this, _shared).handle === void 0 || __privateGet(this, _shared).handle === expiredHandle) {
1440
- const retryCount = this.staleCacheRetryCount + 1;
1441
- return {
1442
- action: `stale-retry`,
1443
- state: new StaleRetryState(__spreadProps(__spreadValues({}, this.currentFields), {
1444
- staleCacheBuster: input.createCacheBuster(),
1445
- staleCacheRetryCount: retryCount
1446
- })),
1447
- exceededMaxRetries: retryCount > input.maxStaleCacheRetries
1448
- };
1449
- }
1450
- return { action: `ignored`, state: this };
1439
+ const retryCount = this.staleCacheRetryCount + 1;
1440
+ return {
1441
+ action: `stale-retry`,
1442
+ state: new StaleRetryState(__spreadProps(__spreadValues({}, this.currentFields), {
1443
+ staleCacheBuster: input.createCacheBuster(),
1444
+ staleCacheRetryCount: retryCount
1445
+ })),
1446
+ exceededMaxRetries: retryCount > input.maxStaleCacheRetries
1447
+ };
1451
1448
  }
1452
1449
  // --- handleMessageBatch: template method with onUpToDate override point ---
1453
1450
  handleMessageBatch(input) {
@@ -1946,7 +1943,7 @@ function canonicalShapeKey(url) {
1946
1943
  cleanUrl.searchParams.sort();
1947
1944
  return cleanUrl.toString();
1948
1945
  }
1949
- 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;
1946
+ 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;
1950
1947
  var ShapeStream = class {
1951
1948
  constructor(options) {
1952
1949
  __privateAdd(this, _ShapeStream_instances);
@@ -1993,7 +1990,8 @@ var ShapeStream = class {
1993
1990
  __privateAdd(this, _fastLoopBackoffMaxMs, 5e3);
1994
1991
  __privateAdd(this, _fastLoopConsecutiveCount, 0);
1995
1992
  __privateAdd(this, _fastLoopMaxCount, 5);
1996
- __privateAdd(this, _refetchCacheBuster);
1993
+ __privateAdd(this, _pendingRequestShapeCacheBuster);
1994
+ __privateAdd(this, _maxSnapshotRetries, 5);
1997
1995
  var _a, _b, _c, _d;
1998
1996
  this.options = __spreadValues({ subscribe: true }, options);
1999
1997
  validateOptions(this.options);
@@ -2151,7 +2149,8 @@ var ShapeStream = class {
2151
2149
  __privateGet(this, _pauseLock).acquire(snapshotReason);
2152
2150
  const snapshotWarnTimer = setTimeout(() => {
2153
2151
  console.warn(
2154
- `[Electric] Snapshot "${snapshotReason}" has held the pause lock for 30s \u2014 possible hung request or leaked lock. Current holders: ${[.../* @__PURE__ */ new Set([snapshotReason])].join(`, `)}`
2152
+ `[Electric] Snapshot "${snapshotReason}" has held the pause lock for 30s \u2014 possible hung request or leaked lock. Current holders: ${[.../* @__PURE__ */ new Set([snapshotReason])].join(`, `)}`,
2153
+ new Error(`stack trace`)
2155
2154
  );
2156
2155
  }, 3e4);
2157
2156
  try {
@@ -2180,7 +2179,8 @@ var ShapeStream = class {
2180
2179
  __privateSet(this, _syncState, transition.state);
2181
2180
  } else {
2182
2181
  console.warn(
2183
- `[Electric] Snapshot response metadata was not accepted by state "${__privateGet(this, _syncState).kind}" (action: ${transition.action}). Stream offset was not advanced from snapshot.`
2182
+ `[Electric] Snapshot response metadata was not accepted by state "${__privateGet(this, _syncState).kind}" (action: ${transition.action}). Stream offset was not advanced from snapshot.`,
2183
+ new Error(`stack trace`)
2184
2184
  );
2185
2185
  }
2186
2186
  }
@@ -2205,64 +2205,7 @@ var ShapeStream = class {
2205
2205
  * @returns The metadata, data, and the response's offset/handle for state advancement.
2206
2206
  */
2207
2207
  async fetchSnapshot(opts) {
2208
- var _a, _b, _c;
2209
- const method = (_b = (_a = opts.method) != null ? _a : this.options.subsetMethod) != null ? _b : `GET`;
2210
- const usePost = method === `POST`;
2211
- let fetchUrl;
2212
- let fetchOptions;
2213
- if (usePost) {
2214
- const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true);
2215
- fetchUrl = result.fetchUrl;
2216
- fetchOptions = {
2217
- method: `POST`,
2218
- headers: __spreadProps(__spreadValues({}, result.requestHeaders), {
2219
- "Content-Type": `application/json`
2220
- }),
2221
- body: bigintSafeStringify(__privateMethod(this, _ShapeStream_instances, buildSubsetBody_fn).call(this, opts))
2222
- };
2223
- } else {
2224
- const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
2225
- fetchUrl = result.fetchUrl;
2226
- fetchOptions = { headers: result.requestHeaders };
2227
- }
2228
- const usedHandle = __privateGet(this, _syncState).handle;
2229
- let response;
2230
- try {
2231
- response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
2232
- } catch (e) {
2233
- if (e instanceof FetchError && e.status === 409) {
2234
- if (usedHandle) {
2235
- const shapeKey = canonicalShapeKey(fetchUrl);
2236
- expiredShapesCache.markExpired(shapeKey, usedHandle);
2237
- }
2238
- const nextHandle = e.headers[SHAPE_HANDLE_HEADER];
2239
- if (nextHandle) {
2240
- __privateSet(this, _syncState, __privateGet(this, _syncState).withHandle(nextHandle));
2241
- } else {
2242
- console.warn(
2243
- `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`
2244
- );
2245
- __privateSet(this, _refetchCacheBuster, createCacheBuster());
2246
- }
2247
- return this.fetchSnapshot(opts);
2248
- }
2249
- throw e;
2250
- }
2251
- if (!response.ok) {
2252
- throw await FetchError.fromResponse(response, fetchUrl.toString());
2253
- }
2254
- const schema = (_c = __privateGet(this, _syncState).schema) != null ? _c : getSchemaFromHeaders(response.headers, {
2255
- required: true,
2256
- url: fetchUrl.toString()
2257
- });
2258
- const { metadata, data: rawData } = await response.json();
2259
- const data = __privateGet(this, _messageParser).parseSnapshotData(
2260
- rawData,
2261
- schema
2262
- );
2263
- const responseOffset = response.headers.get(CHUNK_LAST_OFFSET_HEADER) || null;
2264
- const responseHandle = response.headers.get(SHAPE_HANDLE_HEADER);
2265
- return { metadata, data, responseOffset, responseHandle };
2208
+ return __privateMethod(this, _ShapeStream_instances, fetchSnapshotWithRetry_fn).call(this, opts, 0);
2266
2209
  }
2267
2210
  };
2268
2211
  _error = new WeakMap();
@@ -2304,7 +2247,8 @@ _fastLoopBackoffBaseMs = new WeakMap();
2304
2247
  _fastLoopBackoffMaxMs = new WeakMap();
2305
2248
  _fastLoopConsecutiveCount = new WeakMap();
2306
2249
  _fastLoopMaxCount = new WeakMap();
2307
- _refetchCacheBuster = new WeakMap();
2250
+ _pendingRequestShapeCacheBuster = new WeakMap();
2251
+ _maxSnapshotRetries = new WeakMap();
2308
2252
  start_fn = async function() {
2309
2253
  var _a, _b;
2310
2254
  __privateSet(this, _started, true);
@@ -2356,9 +2300,15 @@ teardown_fn = function() {
2356
2300
  (_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
2357
2301
  (_b = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _b.call(this);
2358
2302
  };
2359
- requestShape_fn = async function() {
2303
+ requestShape_fn = async function(requestShapeCacheBuster) {
2360
2304
  var _a, _b;
2361
- if (__privateGet(this, _pauseLock).isPaused) return;
2305
+ const activeCacheBuster = requestShapeCacheBuster != null ? requestShapeCacheBuster : __privateGet(this, _pendingRequestShapeCacheBuster);
2306
+ if (__privateGet(this, _pauseLock).isPaused) {
2307
+ if (activeCacheBuster) {
2308
+ __privateSet(this, _pendingRequestShapeCacheBuster, activeCacheBuster);
2309
+ }
2310
+ return;
2311
+ }
2362
2312
  if (!this.options.subscribe && (((_a = this.options.signal) == null ? void 0 : _a.aborted) || __privateGet(this, _syncState).isUpToDate)) {
2363
2313
  return;
2364
2314
  }
@@ -2375,15 +2325,23 @@ requestShape_fn = async function() {
2375
2325
  }
2376
2326
  const { url, signal } = this.options;
2377
2327
  const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, url, resumingFromPause);
2328
+ if (activeCacheBuster) {
2329
+ fetchUrl.searchParams.set(CACHE_BUSTER_QUERY_PARAM, activeCacheBuster);
2330
+ fetchUrl.searchParams.sort();
2331
+ }
2378
2332
  const abortListener = await __privateMethod(this, _ShapeStream_instances, createAbortListener_fn).call(this, signal);
2379
2333
  const requestAbortController = __privateGet(this, _requestAbortController);
2380
2334
  if (__privateGet(this, _pauseLock).isPaused) {
2381
2335
  if (abortListener && signal) {
2382
2336
  signal.removeEventListener(`abort`, abortListener);
2383
2337
  }
2338
+ if (activeCacheBuster) {
2339
+ __privateSet(this, _pendingRequestShapeCacheBuster, activeCacheBuster);
2340
+ }
2384
2341
  __privateSet(this, _requestAbortController, void 0);
2385
2342
  return;
2386
2343
  }
2344
+ __privateSet(this, _pendingRequestShapeCacheBuster, void 0);
2387
2345
  try {
2388
2346
  await __privateMethod(this, _ShapeStream_instances, fetchShape_fn).call(this, {
2389
2347
  fetchUrl,
@@ -2410,16 +2368,18 @@ requestShape_fn = async function() {
2410
2368
  expiredShapesCache.markExpired(shapeKey, __privateGet(this, _syncState).handle);
2411
2369
  }
2412
2370
  const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
2371
+ let nextRequestShapeCacheBuster;
2413
2372
  if (!newShapeHandle) {
2414
2373
  console.warn(
2415
- `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`
2374
+ `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`,
2375
+ new Error(`stack trace`)
2416
2376
  );
2417
- __privateSet(this, _refetchCacheBuster, createCacheBuster());
2377
+ nextRequestShapeCacheBuster = createCacheBuster();
2418
2378
  }
2419
2379
  __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
2420
2380
  const messages409 = Array.isArray(e.json) ? e.json : e.json != null ? [e.json] : [];
2421
2381
  await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messages409);
2422
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
2382
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this, nextRequestShapeCacheBuster);
2423
2383
  } else {
2424
2384
  throw e;
2425
2385
  }
@@ -2460,7 +2420,8 @@ For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`
2460
2420
  }
2461
2421
  if (__privateGet(this, _fastLoopConsecutiveCount) === 1) {
2462
2422
  console.warn(
2463
- `[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}`
2423
+ `[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}`,
2424
+ new Error(`stack trace`)
2464
2425
  );
2465
2426
  if (__privateGet(this, _currentFetchUrl)) {
2466
2427
  const shapeKey = canonicalShapeKey(__privateGet(this, _currentFetchUrl));
@@ -2584,13 +2545,6 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
2584
2545
  if (expiredHandle) {
2585
2546
  fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle);
2586
2547
  }
2587
- if (__privateGet(this, _refetchCacheBuster)) {
2588
- fetchUrl.searchParams.set(
2589
- CACHE_BUSTER_QUERY_PARAM,
2590
- __privateGet(this, _refetchCacheBuster)
2591
- );
2592
- __privateSet(this, _refetchCacheBuster, void 0);
2593
- }
2594
2548
  fetchUrl.searchParams.sort();
2595
2549
  return {
2596
2550
  fetchUrl,
@@ -2643,7 +2597,8 @@ onInitialResponse_fn = async function(response) {
2643
2597
  );
2644
2598
  }
2645
2599
  console.warn(
2646
- `[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)}).`
2600
+ `[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)}).`,
2601
+ new Error(`stack trace`)
2647
2602
  );
2648
2603
  throw new StaleCacheError(
2649
2604
  `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.`
@@ -2651,7 +2606,8 @@ onInitialResponse_fn = async function(response) {
2651
2606
  }
2652
2607
  if (transition.action === `ignored`) {
2653
2608
  console.warn(
2654
- `[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}".`
2609
+ `[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.`,
2610
+ new Error(`stack trace`)
2655
2611
  );
2656
2612
  return false;
2657
2613
  }
@@ -2660,7 +2616,8 @@ onInitialResponse_fn = async function(response) {
2660
2616
  onMessages_fn = async function(batch, isSseMessage = false) {
2661
2617
  if (!Array.isArray(batch)) {
2662
2618
  console.warn(
2663
- `[Electric] #onMessages called with non-array argument (${typeof batch}). This is a client bug \u2014 please report it.`
2619
+ `[Electric] #onMessages called with non-array argument (${typeof batch}). This is a client bug \u2014 please report it.`,
2620
+ new Error(`stack trace`)
2664
2621
  );
2665
2622
  return;
2666
2623
  }
@@ -2807,7 +2764,8 @@ requestShapeSSE_fn = async function(opts) {
2807
2764
  __privateSet(this, _syncState, transition.state);
2808
2765
  if (transition.fellBackToLongPolling) {
2809
2766
  console.warn(
2810
- `[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.`
2767
+ `[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.`,
2768
+ new Error(`stack trace`)
2811
2769
  );
2812
2770
  } else if (transition.wasShortConnection) {
2813
2771
  const maxDelay = Math.min(
@@ -2928,6 +2886,86 @@ reset_fn = function(handle) {
2928
2886
  __privateSet(this, _connected, false);
2929
2887
  __privateGet(this, _pauseLock).releaseAllMatching(`snapshot`);
2930
2888
  };
2889
+ fetchSnapshotWithRetry_fn = async function(opts, retryCount, cacheBuster) {
2890
+ var _a, _b, _c;
2891
+ const method = (_b = (_a = opts.method) != null ? _a : this.options.subsetMethod) != null ? _b : `GET`;
2892
+ const usePost = method === `POST`;
2893
+ let fetchUrl;
2894
+ let fetchOptions;
2895
+ if (usePost) {
2896
+ const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true);
2897
+ fetchUrl = result.fetchUrl;
2898
+ fetchOptions = {
2899
+ method: `POST`,
2900
+ headers: __spreadProps(__spreadValues({}, result.requestHeaders), {
2901
+ "Content-Type": `application/json`
2902
+ }),
2903
+ body: bigintSafeStringify(__privateMethod(this, _ShapeStream_instances, buildSubsetBody_fn).call(this, opts))
2904
+ };
2905
+ } else {
2906
+ const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
2907
+ fetchUrl = result.fetchUrl;
2908
+ fetchOptions = { headers: result.requestHeaders };
2909
+ }
2910
+ if (cacheBuster) {
2911
+ fetchUrl.searchParams.set(CACHE_BUSTER_QUERY_PARAM, cacheBuster);
2912
+ fetchUrl.searchParams.sort();
2913
+ }
2914
+ const usedHandle = __privateGet(this, _syncState).handle;
2915
+ let response;
2916
+ try {
2917
+ response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
2918
+ } catch (e) {
2919
+ if (e instanceof FetchError && e.status === 409) {
2920
+ const nextRetryCount = retryCount + 1;
2921
+ if (nextRetryCount > __privateGet(this, _maxSnapshotRetries)) {
2922
+ throw new FetchError(
2923
+ 502,
2924
+ void 0,
2925
+ void 0,
2926
+ {},
2927
+ fetchUrl.toString(),
2928
+ `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}`
2929
+ );
2930
+ }
2931
+ if (usedHandle) {
2932
+ const shapeKey = canonicalShapeKey(fetchUrl);
2933
+ expiredShapesCache.markExpired(shapeKey, usedHandle);
2934
+ }
2935
+ const nextHandle = e.headers[SHAPE_HANDLE_HEADER];
2936
+ let nextCacheBuster;
2937
+ if (nextHandle) {
2938
+ __privateSet(this, _syncState, __privateGet(this, _syncState).withHandle(nextHandle));
2939
+ if (nextHandle === usedHandle) {
2940
+ nextCacheBuster = createCacheBuster();
2941
+ }
2942
+ } else {
2943
+ console.warn(
2944
+ `[Electric] Received 409 response without a shape handle header. This likely indicates a proxy or CDN stripping required headers.`,
2945
+ new Error(`stack trace`)
2946
+ );
2947
+ nextCacheBuster = createCacheBuster();
2948
+ }
2949
+ return __privateMethod(this, _ShapeStream_instances, fetchSnapshotWithRetry_fn).call(this, opts, nextRetryCount, nextCacheBuster);
2950
+ }
2951
+ throw e;
2952
+ }
2953
+ if (!response.ok) {
2954
+ throw await FetchError.fromResponse(response, fetchUrl.toString());
2955
+ }
2956
+ const schema = (_c = __privateGet(this, _syncState).schema) != null ? _c : getSchemaFromHeaders(response.headers, {
2957
+ required: true,
2958
+ url: fetchUrl.toString()
2959
+ });
2960
+ const { metadata, data: rawData } = await response.json();
2961
+ const data = __privateGet(this, _messageParser).parseSnapshotData(
2962
+ rawData,
2963
+ schema
2964
+ );
2965
+ const responseOffset = response.headers.get(CHUNK_LAST_OFFSET_HEADER) || null;
2966
+ const responseHandle = response.headers.get(SHAPE_HANDLE_HEADER);
2967
+ return { metadata, data, responseOffset, responseHandle };
2968
+ };
2931
2969
  buildSubsetBody_fn = function(opts) {
2932
2970
  var _a, _b, _c, _d;
2933
2971
  const body = {};