@electric-sql/client 1.0.4 → 1.0.6

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.
@@ -245,6 +245,13 @@ function isControlMessage(message) {
245
245
  function isUpToDateMessage(message) {
246
246
  return isControlMessage(message) && message.headers.control === `up-to-date`;
247
247
  }
248
+ function getOffset(message) {
249
+ const lsn = message.headers.global_last_seen_lsn;
250
+ if (!lsn) {
251
+ return;
252
+ }
253
+ return `${lsn}_0`;
254
+ }
248
255
 
249
256
  // src/constants.ts
250
257
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
@@ -261,6 +268,7 @@ var TABLE_QUERY_PARAM = `table`;
261
268
  var WHERE_QUERY_PARAM = `where`;
262
269
  var REPLICA_PARAM = `replica`;
263
270
  var WHERE_PARAMS_PARAM = `params`;
271
+ var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
264
272
  var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
265
273
  var PAUSE_STREAM = `pause-stream`;
266
274
 
@@ -289,7 +297,8 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
289
297
  try {
290
298
  const result = await fetchClient(...args);
291
299
  if (result.ok) return result;
292
- else throw await FetchError.fromResponse(result, url.toString());
300
+ const err = await FetchError.fromResponse(result, url.toString());
301
+ throw err;
293
302
  } catch (e) {
294
303
  onFailedAttempt == null ? void 0 : onFailedAttempt();
295
304
  if ((_a = options == null ? void 0 : options.signal) == null ? void 0 : _a.aborted) {
@@ -487,6 +496,9 @@ function noop() {
487
496
  }
488
497
 
489
498
  // src/client.ts
499
+ import {
500
+ fetchEventSource
501
+ } from "@microsoft/fetch-event-source";
490
502
  var RESERVED_PARAMS = /* @__PURE__ */ new Set([
491
503
  LIVE_CACHE_BUSTER_QUERY_PARAM,
492
504
  SHAPE_HANDLE_QUERY_PARAM,
@@ -523,12 +535,14 @@ async function resolveHeaders(headers) {
523
535
  );
524
536
  return Object.fromEntries(resolvedEntries);
525
537
  }
526
- var _error, _fetchClient2, _messageParser, _subscribers, _started, _state, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _ShapeStream_instances, start_fn, requestShape_fn, pause_fn, resume_fn, nextTick_fn, publish_fn, sendErrorToSubscribers_fn, subscribeToVisibilityChanges_fn, reset_fn;
538
+ var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _state, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _ShapeStream_instances, start_fn, requestShape_fn, constructUrl_fn, createAbortListener_fn, onInitialResponse_fn, onMessages_fn, fetchShape_fn, requestShapeLongPoll_fn, requestShapeSSE_fn, pause_fn, resume_fn, nextTick_fn, publish_fn, sendErrorToSubscribers_fn, subscribeToVisibilityChanges_fn, reset_fn;
527
539
  var ShapeStream = class {
540
+ // promise chain for incoming messages
528
541
  constructor(options) {
529
542
  __privateAdd(this, _ShapeStream_instances);
530
543
  __privateAdd(this, _error, null);
531
544
  __privateAdd(this, _fetchClient2);
545
+ __privateAdd(this, _sseFetchClient);
532
546
  __privateAdd(this, _messageParser);
533
547
  __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
534
548
  __privateAdd(this, _started, false);
@@ -548,6 +562,7 @@ var ShapeStream = class {
548
562
  __privateAdd(this, _tickPromise);
549
563
  __privateAdd(this, _tickPromiseResolver);
550
564
  __privateAdd(this, _tickPromiseRejecter);
565
+ __privateAdd(this, _messageChain, Promise.resolve([]));
551
566
  var _a, _b, _c;
552
567
  this.options = __spreadValues({ subscribe: true }, options);
553
568
  validateOptions(this.options);
@@ -557,18 +572,21 @@ var ShapeStream = class {
557
572
  __privateSet(this, _messageParser, new MessageParser(options.parser));
558
573
  __privateSet(this, _onError, this.options.onError);
559
574
  const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
560
- const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
575
+ const backOffOpts = __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
561
576
  onFailedAttempt: () => {
562
577
  var _a2, _b2;
563
578
  __privateSet(this, _connected, false);
564
579
  (_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
565
580
  }
566
- }));
567
- __privateSet(this, _fetchClient2, createFetchWithConsumedMessages(
568
- createFetchWithResponseHeadersCheck(
569
- createFetchWithChunkBuffer(fetchWithBackoffClient)
570
- )
581
+ });
582
+ const fetchWithBackoffClient = createFetchWithBackoff(
583
+ baseFetchClient,
584
+ backOffOpts
585
+ );
586
+ __privateSet(this, _sseFetchClient, createFetchWithResponseHeadersCheck(
587
+ createFetchWithChunkBuffer(fetchWithBackoffClient)
571
588
  ));
589
+ __privateSet(this, _fetchClient2, createFetchWithConsumedMessages(__privateGet(this, _sseFetchClient)));
572
590
  __privateMethod(this, _ShapeStream_instances, subscribeToVisibilityChanges_fn).call(this);
573
591
  }
574
592
  get shapeHandle() {
@@ -636,6 +654,7 @@ var ShapeStream = class {
636
654
  };
637
655
  _error = new WeakMap();
638
656
  _fetchClient2 = new WeakMap();
657
+ _sseFetchClient = new WeakMap();
639
658
  _messageParser = new WeakMap();
640
659
  _subscribers = new WeakMap();
641
660
  _started = new WeakMap();
@@ -653,6 +672,7 @@ _isRefreshing = new WeakMap();
653
672
  _tickPromise = new WeakMap();
654
673
  _tickPromiseResolver = new WeakMap();
655
674
  _tickPromiseRejecter = new WeakMap();
675
+ _messageChain = new WeakMap();
656
676
  _ShapeStream_instances = new WeakSet();
657
677
  start_fn = async function() {
658
678
  var _a;
@@ -683,7 +703,7 @@ start_fn = async function() {
683
703
  }
684
704
  };
685
705
  requestShape_fn = async function() {
686
- var _a, _b, _c, _d;
706
+ var _a, _b;
687
707
  if (__privateGet(this, _state) === `pause-requested`) {
688
708
  __privateSet(this, _state, `paused`);
689
709
  return;
@@ -694,6 +714,46 @@ requestShape_fn = async function() {
694
714
  const resumingFromPause = __privateGet(this, _state) === `paused`;
695
715
  __privateSet(this, _state, `active`);
696
716
  const { url, signal } = this.options;
717
+ const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, url, resumingFromPause);
718
+ const abortListener = await __privateMethod(this, _ShapeStream_instances, createAbortListener_fn).call(this, signal);
719
+ const requestAbortController = __privateGet(this, _requestAbortController);
720
+ try {
721
+ await __privateMethod(this, _ShapeStream_instances, fetchShape_fn).call(this, {
722
+ fetchUrl,
723
+ requestAbortController,
724
+ headers: requestHeaders,
725
+ resumingFromPause
726
+ });
727
+ } catch (e) {
728
+ if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && requestAbortController.signal.aborted && requestAbortController.signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
729
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
730
+ }
731
+ if (e instanceof FetchBackoffAbortError) {
732
+ if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM) {
733
+ __privateSet(this, _state, `paused`);
734
+ }
735
+ return;
736
+ }
737
+ if (!(e instanceof FetchError)) throw e;
738
+ if (e.status == 409) {
739
+ const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
740
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
741
+ await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
742
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
743
+ } else {
744
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
745
+ throw e;
746
+ }
747
+ } finally {
748
+ if (abortListener && signal) {
749
+ signal.removeEventListener(`abort`, abortListener);
750
+ }
751
+ __privateSet(this, _requestAbortController, void 0);
752
+ }
753
+ (_b = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _b.call(this);
754
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
755
+ };
756
+ constructUrl_fn = async function(url, resumingFromPause) {
697
757
  const [requestHeaders, params] = await Promise.all([
698
758
  resolveHeaders(this.options.headers),
699
759
  this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
@@ -734,51 +794,28 @@ requestShape_fn = async function() {
734
794
  fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
735
795
  }
736
796
  fetchUrl.searchParams.sort();
797
+ return {
798
+ fetchUrl,
799
+ requestHeaders
800
+ };
801
+ };
802
+ createAbortListener_fn = async function(signal) {
803
+ var _a;
737
804
  __privateSet(this, _requestAbortController, new AbortController());
738
- let abortListener;
739
805
  if (signal) {
740
- abortListener = () => {
806
+ const abortListener = () => {
741
807
  var _a2;
742
808
  (_a2 = __privateGet(this, _requestAbortController)) == null ? void 0 : _a2.abort(signal.reason);
743
809
  };
744
810
  signal.addEventListener(`abort`, abortListener, { once: true });
745
811
  if (signal.aborted) {
746
- (_b = __privateGet(this, _requestAbortController)) == null ? void 0 : _b.abort(signal.reason);
812
+ (_a = __privateGet(this, _requestAbortController)) == null ? void 0 : _a.abort(signal.reason);
747
813
  }
814
+ return abortListener;
748
815
  }
749
- let response;
750
- try {
751
- response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
752
- signal: __privateGet(this, _requestAbortController).signal,
753
- headers: requestHeaders
754
- });
755
- __privateSet(this, _connected, true);
756
- } catch (e) {
757
- if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && __privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
758
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
759
- }
760
- if (e instanceof FetchBackoffAbortError) {
761
- if (__privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === PAUSE_STREAM) {
762
- __privateSet(this, _state, `paused`);
763
- }
764
- return;
765
- }
766
- if (!(e instanceof FetchError)) throw e;
767
- if (e.status == 409) {
768
- const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
769
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
770
- await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
771
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
772
- } else {
773
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
774
- throw e;
775
- }
776
- } finally {
777
- if (abortListener && signal) {
778
- signal.removeEventListener(`abort`, abortListener);
779
- }
780
- __privateSet(this, _requestAbortController, void 0);
781
- }
816
+ };
817
+ onInitialResponse_fn = async function(response) {
818
+ var _a;
782
819
  const { headers, status } = response;
783
820
  const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
784
821
  if (shapeHandle) {
@@ -796,22 +833,85 @@ requestShape_fn = async function() {
796
833
  const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
797
834
  return schemaHeader ? JSON.parse(schemaHeader) : {};
798
835
  };
799
- __privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
836
+ __privateSet(this, _schema, (_a = __privateGet(this, _schema)) != null ? _a : getSchema());
800
837
  if (status === 204) {
801
838
  __privateSet(this, _lastSyncedAt, Date.now());
802
839
  }
803
- const messages = await response.text() || `[]`;
804
- const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
840
+ };
841
+ onMessages_fn = async function(batch, isSseMessage = false) {
805
842
  if (batch.length > 0) {
806
843
  const lastMessage = batch[batch.length - 1];
807
844
  if (isUpToDateMessage(lastMessage)) {
845
+ if (isSseMessage) {
846
+ const offset = getOffset(lastMessage);
847
+ if (offset) {
848
+ __privateSet(this, _lastOffset, offset);
849
+ }
850
+ }
808
851
  __privateSet(this, _lastSyncedAt, Date.now());
809
852
  __privateSet(this, _isUpToDate, true);
810
853
  }
811
854
  await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
812
855
  }
813
- (_d = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _d.call(this);
814
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
856
+ };
857
+ fetchShape_fn = async function(opts) {
858
+ if (__privateGet(this, _isUpToDate) && this.options.experimentalLiveSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
859
+ opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
860
+ return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
861
+ }
862
+ return __privateMethod(this, _ShapeStream_instances, requestShapeLongPoll_fn).call(this, opts);
863
+ };
864
+ requestShapeLongPoll_fn = async function(opts) {
865
+ const { fetchUrl, requestAbortController, headers } = opts;
866
+ const response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
867
+ signal: requestAbortController.signal,
868
+ headers
869
+ });
870
+ __privateSet(this, _connected, true);
871
+ await __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
872
+ const schema = __privateGet(this, _schema);
873
+ const res = await response.text();
874
+ const messages = res || `[]`;
875
+ const batch = __privateGet(this, _messageParser).parse(messages, schema);
876
+ await __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, batch);
877
+ };
878
+ requestShapeSSE_fn = async function(opts) {
879
+ const { fetchUrl, requestAbortController, headers } = opts;
880
+ const fetch2 = __privateGet(this, _sseFetchClient);
881
+ try {
882
+ let buffer = [];
883
+ await fetchEventSource(fetchUrl.toString(), {
884
+ headers,
885
+ fetch: fetch2,
886
+ onopen: async (response) => {
887
+ __privateSet(this, _connected, true);
888
+ await __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
889
+ },
890
+ onmessage: (event) => {
891
+ if (event.data) {
892
+ const schema = __privateGet(this, _schema);
893
+ const message = __privateGet(this, _messageParser).parse(
894
+ event.data,
895
+ schema
896
+ );
897
+ buffer.push(message);
898
+ if (isUpToDateMessage(message)) {
899
+ __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, buffer, true);
900
+ buffer = [];
901
+ }
902
+ }
903
+ },
904
+ onerror: (error) => {
905
+ throw error;
906
+ },
907
+ signal: requestAbortController.signal
908
+ });
909
+ } catch (error) {
910
+ if (requestAbortController.signal.aborted) {
911
+ throw new FetchBackoffAbortError();
912
+ }
913
+ throw error;
914
+ }
815
915
  };
816
916
  pause_fn = function() {
817
917
  var _a;
@@ -841,17 +941,20 @@ nextTick_fn = async function() {
841
941
  return __privateGet(this, _tickPromise);
842
942
  };
843
943
  publish_fn = async function(messages) {
844
- await Promise.all(
845
- Array.from(__privateGet(this, _subscribers).values()).map(async ([callback, __]) => {
846
- try {
847
- await callback(messages);
848
- } catch (err) {
849
- queueMicrotask(() => {
850
- throw err;
851
- });
852
- }
853
- })
854
- );
944
+ __privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
945
+ () => Promise.all(
946
+ Array.from(__privateGet(this, _subscribers).values()).map(async ([callback, __]) => {
947
+ try {
948
+ await callback(messages);
949
+ } catch (err) {
950
+ queueMicrotask(() => {
951
+ throw err;
952
+ });
953
+ }
954
+ })
955
+ )
956
+ ));
957
+ return __privateGet(this, _messageChain);
855
958
  };
856
959
  sendErrorToSubscribers_fn = function(error) {
857
960
  __privateGet(this, _subscribers).forEach(([_, errorFn]) => {