@electric-sql/client 1.2.2 → 1.3.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/index.d.ts CHANGED
@@ -45,13 +45,22 @@ type MoveOutPattern = {
45
45
  pos: number;
46
46
  value: string;
47
47
  };
48
+ type SubsetParams = {
49
+ where?: string;
50
+ params?: Record<string, string>;
51
+ limit?: number;
52
+ offset?: number;
53
+ orderBy?: string;
54
+ };
48
55
  type ControlMessage = {
49
56
  headers: (Header & {
50
57
  control: `up-to-date` | `must-refetch`;
51
58
  global_last_seen_lsn?: string;
52
59
  }) | (Header & {
53
60
  control: `snapshot-end`;
54
- } & PostgresSnapshot);
61
+ } & PostgresSnapshot) | (Header & {
62
+ control: `subset-end`;
63
+ } & SubsetParams);
55
64
  };
56
65
  type EventMessage = {
57
66
  headers: Header & {
@@ -377,13 +386,6 @@ type ExternalParamsRecord<T extends Row<unknown> = Row> = {
377
386
  } & Partial<PostgresParams<T>> & {
378
387
  [K in ReservedParamKeys]?: never;
379
388
  };
380
- type SubsetParams = {
381
- where?: string;
382
- params?: Record<string, string>;
383
- limit?: number;
384
- offset?: number;
385
- orderBy?: string;
386
- };
387
389
  type ReservedParamKeys = typeof LIVE_CACHE_BUSTER_QUERY_PARAM | typeof SHAPE_HANDLE_QUERY_PARAM | typeof LIVE_QUERY_PARAM | typeof OFFSET_QUERY_PARAM | `subset__${string}`;
388
390
  /**
389
391
  * External headers type - what users provide.
@@ -429,11 +429,9 @@ function isUpToDateMessage(message) {
429
429
  return isControlMessage(message) && message.headers.control === `up-to-date`;
430
430
  }
431
431
  function getOffset(message) {
432
+ if (message.headers.control != `up-to-date`) return;
432
433
  const lsn = message.headers.global_last_seen_lsn;
433
- if (!lsn) {
434
- return;
435
- }
436
- return `${lsn}_0`;
434
+ return lsn ? `${lsn}_0` : void 0;
437
435
  }
438
436
  function isVisibleInSnapshot(txid, snapshot) {
439
437
  const xid = BigInt(txid);
@@ -603,6 +601,7 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
603
601
  return prefetchedRequest;
604
602
  }
605
603
  prefetchQueue == null ? void 0 : prefetchQueue.abort();
604
+ prefetchQueue = void 0;
606
605
  const response = await fetchClient(...args);
607
606
  const nextUrl = getNextChunkUrl(url, response);
608
607
  if (nextUrl) {
@@ -675,12 +674,17 @@ var PrefetchQueue = class {
675
674
  }
676
675
  abort() {
677
676
  __privateGet(this, _prefetchQueue).forEach(([_, aborter]) => aborter.abort());
677
+ __privateGet(this, _prefetchQueue).clear();
678
678
  }
679
679
  consume(...args) {
680
- var _a;
681
680
  const url = args[0].toString();
682
- const request = (_a = __privateGet(this, _prefetchQueue).get(url)) == null ? void 0 : _a[0];
683
- if (!request || url !== __privateGet(this, _queueHeadUrl)) return;
681
+ const entry = __privateGet(this, _prefetchQueue).get(url);
682
+ if (!entry || url !== __privateGet(this, _queueHeadUrl)) return;
683
+ const [request, aborter] = entry;
684
+ if (aborter.signal.aborted) {
685
+ __privateGet(this, _prefetchQueue).delete(url);
686
+ return;
687
+ }
684
688
  __privateGet(this, _prefetchQueue).delete(url);
685
689
  request.then((response) => {
686
690
  const nextUrl = getNextChunkUrl(url, response);
@@ -729,6 +733,13 @@ function getNextChunkUrl(url, res) {
729
733
  if (!shapeHandle || !lastOffset || isUpToDate) return;
730
734
  const nextUrl = new URL(url);
731
735
  if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
736
+ const expiredHandle = nextUrl.searchParams.get(EXPIRED_HANDLE_QUERY_PARAM);
737
+ if (expiredHandle && shapeHandle === expiredHandle) {
738
+ console.warn(
739
+ `[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. Skipping prefetch to prevent infinite 409 loop.`
740
+ );
741
+ return;
742
+ }
732
743
  nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle);
733
744
  nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
734
745
  nextUrl.searchParams.sort();
@@ -1231,7 +1242,8 @@ var ShapeStream = class {
1231
1242
  }
1232
1243
  const { metadata, data } = await this.fetchSnapshot(opts);
1233
1244
  const dataWithEndBoundary = data.concat([
1234
- { headers: __spreadValues({ control: `snapshot-end` }, metadata) }
1245
+ { headers: __spreadValues({ control: `snapshot-end` }, metadata) },
1246
+ { headers: __spreadValues({ control: `subset-end` }, opts) }
1235
1247
  ]);
1236
1248
  __privateGet(this, _snapshotTracker).addSnapshot(
1237
1249
  metadata,
@@ -1536,7 +1548,15 @@ onInitialResponse_fn = async function(response) {
1536
1548
  const { headers, status } = response;
1537
1549
  const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
1538
1550
  if (shapeHandle) {
1539
- __privateSet(this, _shapeHandle, shapeHandle);
1551
+ const shapeKey = __privateGet(this, _currentFetchUrl) ? canonicalShapeKey(__privateGet(this, _currentFetchUrl)) : null;
1552
+ const expiredHandle = shapeKey ? expiredShapesCache.getExpiredHandle(shapeKey) : null;
1553
+ if (shapeHandle !== expiredHandle) {
1554
+ __privateSet(this, _shapeHandle, shapeHandle);
1555
+ } else {
1556
+ console.warn(
1557
+ `[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 handle and continuing with handle "${__privateGet(this, _shapeHandle)}".`
1558
+ );
1559
+ }
1540
1560
  }
1541
1561
  const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
1542
1562
  if (lastOffset) {
@@ -1691,7 +1711,11 @@ pause_fn = function() {
1691
1711
  }
1692
1712
  };
1693
1713
  resume_fn = function() {
1714
+ var _a;
1694
1715
  if (__privateGet(this, _started) && (__privateGet(this, _state) === `paused` || __privateGet(this, _state) === `pause-requested`)) {
1716
+ if ((_a = this.options.signal) == null ? void 0 : _a.aborted) {
1717
+ return;
1718
+ }
1695
1719
  if (__privateGet(this, _state) === `pause-requested`) {
1696
1720
  __privateSet(this, _state, `active`);
1697
1721
  }