@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.
@@ -296,6 +296,13 @@ function isControlMessage(message) {
296
296
  function isUpToDateMessage(message) {
297
297
  return isControlMessage(message) && message.headers.control === `up-to-date`;
298
298
  }
299
+ function getOffset(message) {
300
+ const lsn = message.headers.global_last_seen_lsn;
301
+ if (!lsn) {
302
+ return;
303
+ }
304
+ return `${lsn}_0`;
305
+ }
299
306
 
300
307
  // src/constants.ts
301
308
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
@@ -312,6 +319,7 @@ var TABLE_QUERY_PARAM = `table`;
312
319
  var WHERE_QUERY_PARAM = `where`;
313
320
  var REPLICA_PARAM = `replica`;
314
321
  var WHERE_PARAMS_PARAM = `params`;
322
+ var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
315
323
  var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
316
324
  var PAUSE_STREAM = `pause-stream`;
317
325
 
@@ -340,7 +348,8 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
340
348
  try {
341
349
  const result = yield fetchClient(...args);
342
350
  if (result.ok) return result;
343
- else throw yield FetchError.fromResponse(result, url.toString());
351
+ const err = yield FetchError.fromResponse(result, url.toString());
352
+ throw err;
344
353
  } catch (e) {
345
354
  onFailedAttempt == null ? void 0 : onFailedAttempt();
346
355
  if ((_a = options == null ? void 0 : options.signal) == null ? void 0 : _a.aborted) {
@@ -538,6 +547,7 @@ function noop() {
538
547
  }
539
548
 
540
549
  // src/client.ts
550
+ var import_fetch_event_source = require("@microsoft/fetch-event-source");
541
551
  var RESERVED_PARAMS = /* @__PURE__ */ new Set([
542
552
  LIVE_CACHE_BUSTER_QUERY_PARAM,
543
553
  SHAPE_HANDLE_QUERY_PARAM,
@@ -582,12 +592,14 @@ function resolveHeaders(headers) {
582
592
  return Object.fromEntries(resolvedEntries);
583
593
  });
584
594
  }
585
- 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;
595
+ 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;
586
596
  var ShapeStream = class {
597
+ // promise chain for incoming messages
587
598
  constructor(options) {
588
599
  __privateAdd(this, _ShapeStream_instances);
589
600
  __privateAdd(this, _error, null);
590
601
  __privateAdd(this, _fetchClient2);
602
+ __privateAdd(this, _sseFetchClient);
591
603
  __privateAdd(this, _messageParser);
592
604
  __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
593
605
  __privateAdd(this, _started, false);
@@ -607,6 +619,7 @@ var ShapeStream = class {
607
619
  __privateAdd(this, _tickPromise);
608
620
  __privateAdd(this, _tickPromiseResolver);
609
621
  __privateAdd(this, _tickPromiseRejecter);
622
+ __privateAdd(this, _messageChain, Promise.resolve([]));
610
623
  var _a, _b, _c;
611
624
  this.options = __spreadValues({ subscribe: true }, options);
612
625
  validateOptions(this.options);
@@ -616,18 +629,21 @@ var ShapeStream = class {
616
629
  __privateSet(this, _messageParser, new MessageParser(options.parser));
617
630
  __privateSet(this, _onError, this.options.onError);
618
631
  const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
619
- const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
632
+ const backOffOpts = __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
620
633
  onFailedAttempt: () => {
621
634
  var _a2, _b2;
622
635
  __privateSet(this, _connected, false);
623
636
  (_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
624
637
  }
625
- }));
626
- __privateSet(this, _fetchClient2, createFetchWithConsumedMessages(
627
- createFetchWithResponseHeadersCheck(
628
- createFetchWithChunkBuffer(fetchWithBackoffClient)
629
- )
638
+ });
639
+ const fetchWithBackoffClient = createFetchWithBackoff(
640
+ baseFetchClient,
641
+ backOffOpts
642
+ );
643
+ __privateSet(this, _sseFetchClient, createFetchWithResponseHeadersCheck(
644
+ createFetchWithChunkBuffer(fetchWithBackoffClient)
630
645
  ));
646
+ __privateSet(this, _fetchClient2, createFetchWithConsumedMessages(__privateGet(this, _sseFetchClient)));
631
647
  __privateMethod(this, _ShapeStream_instances, subscribeToVisibilityChanges_fn).call(this);
632
648
  }
633
649
  get shapeHandle() {
@@ -697,6 +713,7 @@ var ShapeStream = class {
697
713
  };
698
714
  _error = new WeakMap();
699
715
  _fetchClient2 = new WeakMap();
716
+ _sseFetchClient = new WeakMap();
700
717
  _messageParser = new WeakMap();
701
718
  _subscribers = new WeakMap();
702
719
  _started = new WeakMap();
@@ -714,6 +731,7 @@ _isRefreshing = new WeakMap();
714
731
  _tickPromise = new WeakMap();
715
732
  _tickPromiseResolver = new WeakMap();
716
733
  _tickPromiseRejecter = new WeakMap();
734
+ _messageChain = new WeakMap();
717
735
  _ShapeStream_instances = new WeakSet();
718
736
  start_fn = function() {
719
737
  return __async(this, null, function* () {
@@ -747,7 +765,7 @@ start_fn = function() {
747
765
  };
748
766
  requestShape_fn = function() {
749
767
  return __async(this, null, function* () {
750
- var _a, _b, _c, _d;
768
+ var _a, _b;
751
769
  if (__privateGet(this, _state) === `pause-requested`) {
752
770
  __privateSet(this, _state, `paused`);
753
771
  return;
@@ -758,6 +776,48 @@ requestShape_fn = function() {
758
776
  const resumingFromPause = __privateGet(this, _state) === `paused`;
759
777
  __privateSet(this, _state, `active`);
760
778
  const { url, signal } = this.options;
779
+ const { fetchUrl, requestHeaders } = yield __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, url, resumingFromPause);
780
+ const abortListener = yield __privateMethod(this, _ShapeStream_instances, createAbortListener_fn).call(this, signal);
781
+ const requestAbortController = __privateGet(this, _requestAbortController);
782
+ try {
783
+ yield __privateMethod(this, _ShapeStream_instances, fetchShape_fn).call(this, {
784
+ fetchUrl,
785
+ requestAbortController,
786
+ headers: requestHeaders,
787
+ resumingFromPause
788
+ });
789
+ } catch (e) {
790
+ if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && requestAbortController.signal.aborted && requestAbortController.signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
791
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
792
+ }
793
+ if (e instanceof FetchBackoffAbortError) {
794
+ if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM) {
795
+ __privateSet(this, _state, `paused`);
796
+ }
797
+ return;
798
+ }
799
+ if (!(e instanceof FetchError)) throw e;
800
+ if (e.status == 409) {
801
+ const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
802
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
803
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
804
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
805
+ } else {
806
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
807
+ throw e;
808
+ }
809
+ } finally {
810
+ if (abortListener && signal) {
811
+ signal.removeEventListener(`abort`, abortListener);
812
+ }
813
+ __privateSet(this, _requestAbortController, void 0);
814
+ }
815
+ (_b = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _b.call(this);
816
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
817
+ });
818
+ };
819
+ constructUrl_fn = function(url, resumingFromPause) {
820
+ return __async(this, null, function* () {
761
821
  const [requestHeaders, params] = yield Promise.all([
762
822
  resolveHeaders(this.options.headers),
763
823
  this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
@@ -798,51 +858,32 @@ requestShape_fn = function() {
798
858
  fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
799
859
  }
800
860
  fetchUrl.searchParams.sort();
861
+ return {
862
+ fetchUrl,
863
+ requestHeaders
864
+ };
865
+ });
866
+ };
867
+ createAbortListener_fn = function(signal) {
868
+ return __async(this, null, function* () {
869
+ var _a;
801
870
  __privateSet(this, _requestAbortController, new AbortController());
802
- let abortListener;
803
871
  if (signal) {
804
- abortListener = () => {
872
+ const abortListener = () => {
805
873
  var _a2;
806
874
  (_a2 = __privateGet(this, _requestAbortController)) == null ? void 0 : _a2.abort(signal.reason);
807
875
  };
808
876
  signal.addEventListener(`abort`, abortListener, { once: true });
809
877
  if (signal.aborted) {
810
- (_b = __privateGet(this, _requestAbortController)) == null ? void 0 : _b.abort(signal.reason);
811
- }
812
- }
813
- let response;
814
- try {
815
- response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
816
- signal: __privateGet(this, _requestAbortController).signal,
817
- headers: requestHeaders
818
- });
819
- __privateSet(this, _connected, true);
820
- } catch (e) {
821
- if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && __privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
822
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
823
- }
824
- if (e instanceof FetchBackoffAbortError) {
825
- if (__privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === PAUSE_STREAM) {
826
- __privateSet(this, _state, `paused`);
827
- }
828
- return;
829
- }
830
- if (!(e instanceof FetchError)) throw e;
831
- if (e.status == 409) {
832
- const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
833
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
834
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
835
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
836
- } else {
837
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
838
- throw e;
878
+ (_a = __privateGet(this, _requestAbortController)) == null ? void 0 : _a.abort(signal.reason);
839
879
  }
840
- } finally {
841
- if (abortListener && signal) {
842
- signal.removeEventListener(`abort`, abortListener);
843
- }
844
- __privateSet(this, _requestAbortController, void 0);
880
+ return abortListener;
845
881
  }
882
+ });
883
+ };
884
+ onInitialResponse_fn = function(response) {
885
+ return __async(this, null, function* () {
886
+ var _a;
846
887
  const { headers, status } = response;
847
888
  const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
848
889
  if (shapeHandle) {
@@ -860,22 +901,93 @@ requestShape_fn = function() {
860
901
  const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
861
902
  return schemaHeader ? JSON.parse(schemaHeader) : {};
862
903
  };
863
- __privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
904
+ __privateSet(this, _schema, (_a = __privateGet(this, _schema)) != null ? _a : getSchema());
864
905
  if (status === 204) {
865
906
  __privateSet(this, _lastSyncedAt, Date.now());
866
907
  }
867
- const messages = (yield response.text()) || `[]`;
868
- const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
908
+ });
909
+ };
910
+ onMessages_fn = function(batch, isSseMessage = false) {
911
+ return __async(this, null, function* () {
869
912
  if (batch.length > 0) {
870
913
  const lastMessage = batch[batch.length - 1];
871
914
  if (isUpToDateMessage(lastMessage)) {
915
+ if (isSseMessage) {
916
+ const offset = getOffset(lastMessage);
917
+ if (offset) {
918
+ __privateSet(this, _lastOffset, offset);
919
+ }
920
+ }
872
921
  __privateSet(this, _lastSyncedAt, Date.now());
873
922
  __privateSet(this, _isUpToDate, true);
874
923
  }
875
924
  yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
876
925
  }
877
- (_d = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _d.call(this);
878
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
926
+ });
927
+ };
928
+ fetchShape_fn = function(opts) {
929
+ return __async(this, null, function* () {
930
+ if (__privateGet(this, _isUpToDate) && this.options.experimentalLiveSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
931
+ opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
932
+ return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
933
+ }
934
+ return __privateMethod(this, _ShapeStream_instances, requestShapeLongPoll_fn).call(this, opts);
935
+ });
936
+ };
937
+ requestShapeLongPoll_fn = function(opts) {
938
+ return __async(this, null, function* () {
939
+ const { fetchUrl, requestAbortController, headers } = opts;
940
+ const response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
941
+ signal: requestAbortController.signal,
942
+ headers
943
+ });
944
+ __privateSet(this, _connected, true);
945
+ yield __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
946
+ const schema = __privateGet(this, _schema);
947
+ const res = yield response.text();
948
+ const messages = res || `[]`;
949
+ const batch = __privateGet(this, _messageParser).parse(messages, schema);
950
+ yield __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, batch);
951
+ });
952
+ };
953
+ requestShapeSSE_fn = function(opts) {
954
+ return __async(this, null, function* () {
955
+ const { fetchUrl, requestAbortController, headers } = opts;
956
+ const fetch2 = __privateGet(this, _sseFetchClient);
957
+ try {
958
+ let buffer = [];
959
+ yield (0, import_fetch_event_source.fetchEventSource)(fetchUrl.toString(), {
960
+ headers,
961
+ fetch: fetch2,
962
+ onopen: (response) => __async(this, null, function* () {
963
+ __privateSet(this, _connected, true);
964
+ yield __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
965
+ }),
966
+ onmessage: (event) => {
967
+ if (event.data) {
968
+ const schema = __privateGet(this, _schema);
969
+ const message = __privateGet(this, _messageParser).parse(
970
+ event.data,
971
+ schema
972
+ );
973
+ buffer.push(message);
974
+ if (isUpToDateMessage(message)) {
975
+ __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, buffer, true);
976
+ buffer = [];
977
+ }
978
+ }
979
+ },
980
+ onerror: (error) => {
981
+ throw error;
982
+ },
983
+ signal: requestAbortController.signal
984
+ });
985
+ } catch (error) {
986
+ if (requestAbortController.signal.aborted) {
987
+ throw new FetchBackoffAbortError();
988
+ }
989
+ throw error;
990
+ }
879
991
  });
880
992
  };
881
993
  pause_fn = function() {
@@ -909,17 +1021,20 @@ nextTick_fn = function() {
909
1021
  };
910
1022
  publish_fn = function(messages) {
911
1023
  return __async(this, null, function* () {
912
- yield Promise.all(
913
- Array.from(__privateGet(this, _subscribers).values()).map((_0) => __async(this, [_0], function* ([callback, __]) {
914
- try {
915
- yield callback(messages);
916
- } catch (err) {
917
- queueMicrotask(() => {
918
- throw err;
919
- });
920
- }
921
- }))
922
- );
1024
+ __privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
1025
+ () => Promise.all(
1026
+ Array.from(__privateGet(this, _subscribers).values()).map((_0) => __async(this, [_0], function* ([callback, __]) {
1027
+ try {
1028
+ yield callback(messages);
1029
+ } catch (err) {
1030
+ queueMicrotask(() => {
1031
+ throw err;
1032
+ });
1033
+ }
1034
+ }))
1035
+ )
1036
+ ));
1037
+ return __privateGet(this, _messageChain);
923
1038
  });
924
1039
  };
925
1040
  sendErrorToSubscribers_fn = function(error) {