@electric-sql/client 1.5.5 → 1.5.7

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
@@ -439,6 +439,12 @@ function getOffset(message) {
439
439
  const lsn = message.headers.global_last_seen_lsn;
440
440
  return lsn ? `${lsn}_0` : void 0;
441
441
  }
442
+ function bigintReplacer(_key, value) {
443
+ return typeof value === `bigint` ? value.toString() : value;
444
+ }
445
+ function bigintSafeStringify(value) {
446
+ return JSON.stringify(value, bigintReplacer);
447
+ }
442
448
  function isVisibleInSnapshot(txid, snapshot) {
443
449
  const xid = BigInt(txid);
444
450
  const xmin = BigInt(snapshot.xmin);
@@ -609,6 +615,12 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
609
615
  let prefetchQueue;
610
616
  const prefetchClient = async (...args) => {
611
617
  const url = args[0].toString();
618
+ const method = getRequestMethod(args[0], args[1]);
619
+ if (method !== `GET`) {
620
+ prefetchQueue == null ? void 0 : prefetchQueue.abort();
621
+ prefetchQueue = void 0;
622
+ return fetchClient(...args);
623
+ }
612
624
  const prefetchedRequest = prefetchQueue == null ? void 0 : prefetchQueue.consume(...args);
613
625
  if (prefetchedRequest) {
614
626
  return prefetchedRequest;
@@ -778,6 +790,15 @@ function chainAborter(aborter, sourceSignal) {
778
790
  }
779
791
  function noop() {
780
792
  }
793
+ function getRequestMethod(input, init) {
794
+ if (init == null ? void 0 : init.method) {
795
+ return init.method.toUpperCase();
796
+ }
797
+ if (typeof Request !== `undefined` && input instanceof Request) {
798
+ return input.method.toUpperCase();
799
+ }
800
+ return `GET`;
801
+ }
781
802
 
782
803
  // src/expression-compiler.ts
783
804
  function compileExpression(expr, columnMapper) {
@@ -1454,6 +1475,12 @@ var FetchingState = class extends ActiveState {
1454
1475
  const staleResult = this.checkStaleResponse(input);
1455
1476
  if (staleResult) return staleResult;
1456
1477
  const shared = this.parseResponseFields(input);
1478
+ if (input.status === 204) {
1479
+ return {
1480
+ action: `accepted`,
1481
+ state: new LiveState(shared, { sseFallbackToLongPolling: true })
1482
+ };
1483
+ }
1457
1484
  return { action: `accepted`, state: new SyncingState(shared) };
1458
1485
  }
1459
1486
  canEnterReplayMode() {
@@ -1683,6 +1710,13 @@ var PausedState = class _PausedState extends ShapeStreamState {
1683
1710
  get replayCursor() {
1684
1711
  return this.previousState.replayCursor;
1685
1712
  }
1713
+ handleResponseMetadata(input) {
1714
+ const transition = this.previousState.handleResponseMetadata(input);
1715
+ if (transition.action === `accepted`) {
1716
+ return { action: `accepted`, state: new _PausedState(transition.state) };
1717
+ }
1718
+ return transition;
1719
+ }
1686
1720
  withHandle(handle) {
1687
1721
  return new _PausedState(this.previousState.withHandle(handle));
1688
1722
  }
@@ -2073,7 +2107,7 @@ var ShapeStream = class {
2073
2107
  );
2074
2108
  }, 3e4);
2075
2109
  try {
2076
- const { metadata, data } = await this.fetchSnapshot(opts);
2110
+ const { metadata, data, responseOffset, responseHandle } = await this.fetchSnapshot(opts);
2077
2111
  const dataWithEndBoundary = data.concat([
2078
2112
  { headers: __spreadValues({ control: `snapshot-end` }, metadata) },
2079
2113
  { headers: __spreadValues({ control: `subset-end` }, opts) }
@@ -2083,6 +2117,25 @@ var ShapeStream = class {
2083
2117
  new Set(data.map((message) => message.key))
2084
2118
  );
2085
2119
  __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, dataWithEndBoundary, false);
2120
+ if (responseOffset !== null || responseHandle !== null) {
2121
+ const transition = __privateGet(this, _syncState).handleResponseMetadata({
2122
+ status: 200,
2123
+ responseHandle,
2124
+ responseOffset,
2125
+ responseCursor: null,
2126
+ expiredHandle: null,
2127
+ now: Date.now(),
2128
+ maxStaleCacheRetries: __privateGet(this, _maxStaleCacheRetries),
2129
+ createCacheBuster: () => `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
2130
+ });
2131
+ if (transition.action === `accepted`) {
2132
+ __privateSet(this, _syncState, transition.state);
2133
+ } else {
2134
+ console.warn(
2135
+ `[Electric] Snapshot response metadata was not accepted by state "${__privateGet(this, _syncState).kind}" (action: ${transition.action}). Stream offset was not advanced from snapshot.`
2136
+ );
2137
+ }
2138
+ }
2086
2139
  return {
2087
2140
  metadata,
2088
2141
  data
@@ -2101,7 +2154,7 @@ var ShapeStream = class {
2101
2154
  * `subsetMethod: 'POST'` on the stream to send parameters in the request body instead.
2102
2155
  *
2103
2156
  * @param opts - The options for the snapshot request.
2104
- * @returns The metadata and the data for the snapshot.
2157
+ * @returns The metadata, data, and the response's offset/handle for state advancement.
2105
2158
  */
2106
2159
  async fetchSnapshot(opts) {
2107
2160
  var _a, _b, _c;
@@ -2117,7 +2170,7 @@ var ShapeStream = class {
2117
2170
  headers: __spreadProps(__spreadValues({}, result.requestHeaders), {
2118
2171
  "Content-Type": `application/json`
2119
2172
  }),
2120
- body: JSON.stringify(__privateMethod(this, _ShapeStream_instances, buildSubsetBody_fn).call(this, opts))
2173
+ body: bigintSafeStringify(__privateMethod(this, _ShapeStream_instances, buildSubsetBody_fn).call(this, opts))
2121
2174
  };
2122
2175
  } else {
2123
2176
  const result = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
@@ -2152,7 +2205,9 @@ var ShapeStream = class {
2152
2205
  rawData,
2153
2206
  schema
2154
2207
  );
2155
- return { metadata, data };
2208
+ const responseOffset = response.headers.get(CHUNK_LAST_OFFSET_HEADER) || null;
2209
+ const responseHandle = response.headers.get(SHAPE_HANDLE_HEADER);
2210
+ return { metadata, data, responseOffset, responseHandle };
2156
2211
  }
2157
2212
  };
2158
2213
  _error = new WeakMap();
@@ -2365,7 +2420,7 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
2365
2420
  if (subsetParams.params)
2366
2421
  fetchUrl.searchParams.set(
2367
2422
  SUBSET_PARAM_WHERE_PARAMS,
2368
- JSON.stringify(subsetParams.params)
2423
+ bigintSafeStringify(subsetParams.params)
2369
2424
  );
2370
2425
  if (subsetParams.limit)
2371
2426
  setQueryParam(fetchUrl, SUBSET_PARAM_LIMIT, subsetParams.limit);
@@ -2544,7 +2599,7 @@ requestShapeLongPoll_fn = async function(opts) {
2544
2599
  const messages = res || `[]`;
2545
2600
  const batch = __privateGet(this, _messageParser).parse(messages, schema);
2546
2601
  if (!Array.isArray(batch)) {
2547
- const preview = (_a = JSON.stringify(batch)) == null ? void 0 : _a.slice(0, 200);
2602
+ const preview = (_a = bigintSafeStringify(batch)) == null ? void 0 : _a.slice(0, 200);
2548
2603
  throw new FetchError(
2549
2604
  response.status,
2550
2605
  `Received non-array response body from shape endpoint. This may indicate a proxy or CDN is returning an unexpected response. Expected a JSON array, got ${typeof batch}: ${preview}`,
@@ -2602,7 +2657,9 @@ requestShapeSSE_fn = async function(opts) {
2602
2657
  if (requestAbortController.signal.aborted) {
2603
2658
  throw new FetchBackoffAbortError();
2604
2659
  }
2605
- throw error;
2660
+ if (error instanceof FetchError || error instanceof StaleCacheError || error instanceof MissingHeadersError) {
2661
+ throw error;
2662
+ }
2606
2663
  } finally {
2607
2664
  const connectionDuration = Date.now() - __privateGet(this, _lastSseConnectionStartTime);
2608
2665
  const wasAborted = requestAbortController.signal.aborted;
@@ -2906,7 +2963,7 @@ var Shape = class {
2906
2963
  * Returns void; data will be emitted via the stream and processed by this Shape.
2907
2964
  */
2908
2965
  async requestSnapshot(params) {
2909
- const key = JSON.stringify(params);
2966
+ const key = bigintSafeStringify(params);
2910
2967
  __privateGet(this, _requestedSubSnapshots).add(key);
2911
2968
  await __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
2912
2969
  await this.stream.requestSnapshot(params);