@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.
package/dist/index.mjs CHANGED
@@ -267,6 +267,13 @@ function isControlMessage(message) {
267
267
  function isUpToDateMessage(message) {
268
268
  return isControlMessage(message) && message.headers.control === `up-to-date`;
269
269
  }
270
+ function getOffset(message) {
271
+ const lsn = message.headers.global_last_seen_lsn;
272
+ if (!lsn) {
273
+ return;
274
+ }
275
+ return `${lsn}_0`;
276
+ }
270
277
 
271
278
  // src/constants.ts
272
279
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
@@ -283,6 +290,7 @@ var TABLE_QUERY_PARAM = `table`;
283
290
  var WHERE_QUERY_PARAM = `where`;
284
291
  var REPLICA_PARAM = `replica`;
285
292
  var WHERE_PARAMS_PARAM = `params`;
293
+ var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
286
294
  var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
287
295
  var PAUSE_STREAM = `pause-stream`;
288
296
 
@@ -311,7 +319,8 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
311
319
  try {
312
320
  const result = yield fetchClient(...args);
313
321
  if (result.ok) return result;
314
- else throw yield FetchError.fromResponse(result, url.toString());
322
+ const err = yield FetchError.fromResponse(result, url.toString());
323
+ throw err;
315
324
  } catch (e) {
316
325
  onFailedAttempt == null ? void 0 : onFailedAttempt();
317
326
  if ((_a = options == null ? void 0 : options.signal) == null ? void 0 : _a.aborted) {
@@ -509,6 +518,9 @@ function noop() {
509
518
  }
510
519
 
511
520
  // src/client.ts
521
+ import {
522
+ fetchEventSource
523
+ } from "@microsoft/fetch-event-source";
512
524
  var RESERVED_PARAMS = /* @__PURE__ */ new Set([
513
525
  LIVE_CACHE_BUSTER_QUERY_PARAM,
514
526
  SHAPE_HANDLE_QUERY_PARAM,
@@ -553,12 +565,14 @@ function resolveHeaders(headers) {
553
565
  return Object.fromEntries(resolvedEntries);
554
566
  });
555
567
  }
556
- 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;
568
+ 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;
557
569
  var ShapeStream = class {
570
+ // promise chain for incoming messages
558
571
  constructor(options) {
559
572
  __privateAdd(this, _ShapeStream_instances);
560
573
  __privateAdd(this, _error, null);
561
574
  __privateAdd(this, _fetchClient2);
575
+ __privateAdd(this, _sseFetchClient);
562
576
  __privateAdd(this, _messageParser);
563
577
  __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
564
578
  __privateAdd(this, _started, false);
@@ -578,6 +592,7 @@ var ShapeStream = class {
578
592
  __privateAdd(this, _tickPromise);
579
593
  __privateAdd(this, _tickPromiseResolver);
580
594
  __privateAdd(this, _tickPromiseRejecter);
595
+ __privateAdd(this, _messageChain, Promise.resolve([]));
581
596
  var _a, _b, _c;
582
597
  this.options = __spreadValues({ subscribe: true }, options);
583
598
  validateOptions(this.options);
@@ -587,18 +602,21 @@ var ShapeStream = class {
587
602
  __privateSet(this, _messageParser, new MessageParser(options.parser));
588
603
  __privateSet(this, _onError, this.options.onError);
589
604
  const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
590
- const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
605
+ const backOffOpts = __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
591
606
  onFailedAttempt: () => {
592
607
  var _a2, _b2;
593
608
  __privateSet(this, _connected, false);
594
609
  (_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
595
610
  }
596
- }));
597
- __privateSet(this, _fetchClient2, createFetchWithConsumedMessages(
598
- createFetchWithResponseHeadersCheck(
599
- createFetchWithChunkBuffer(fetchWithBackoffClient)
600
- )
611
+ });
612
+ const fetchWithBackoffClient = createFetchWithBackoff(
613
+ baseFetchClient,
614
+ backOffOpts
615
+ );
616
+ __privateSet(this, _sseFetchClient, createFetchWithResponseHeadersCheck(
617
+ createFetchWithChunkBuffer(fetchWithBackoffClient)
601
618
  ));
619
+ __privateSet(this, _fetchClient2, createFetchWithConsumedMessages(__privateGet(this, _sseFetchClient)));
602
620
  __privateMethod(this, _ShapeStream_instances, subscribeToVisibilityChanges_fn).call(this);
603
621
  }
604
622
  get shapeHandle() {
@@ -668,6 +686,7 @@ var ShapeStream = class {
668
686
  };
669
687
  _error = new WeakMap();
670
688
  _fetchClient2 = new WeakMap();
689
+ _sseFetchClient = new WeakMap();
671
690
  _messageParser = new WeakMap();
672
691
  _subscribers = new WeakMap();
673
692
  _started = new WeakMap();
@@ -685,6 +704,7 @@ _isRefreshing = new WeakMap();
685
704
  _tickPromise = new WeakMap();
686
705
  _tickPromiseResolver = new WeakMap();
687
706
  _tickPromiseRejecter = new WeakMap();
707
+ _messageChain = new WeakMap();
688
708
  _ShapeStream_instances = new WeakSet();
689
709
  start_fn = function() {
690
710
  return __async(this, null, function* () {
@@ -718,7 +738,7 @@ start_fn = function() {
718
738
  };
719
739
  requestShape_fn = function() {
720
740
  return __async(this, null, function* () {
721
- var _a, _b, _c, _d;
741
+ var _a, _b;
722
742
  if (__privateGet(this, _state) === `pause-requested`) {
723
743
  __privateSet(this, _state, `paused`);
724
744
  return;
@@ -729,6 +749,48 @@ requestShape_fn = function() {
729
749
  const resumingFromPause = __privateGet(this, _state) === `paused`;
730
750
  __privateSet(this, _state, `active`);
731
751
  const { url, signal } = this.options;
752
+ const { fetchUrl, requestHeaders } = yield __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, url, resumingFromPause);
753
+ const abortListener = yield __privateMethod(this, _ShapeStream_instances, createAbortListener_fn).call(this, signal);
754
+ const requestAbortController = __privateGet(this, _requestAbortController);
755
+ try {
756
+ yield __privateMethod(this, _ShapeStream_instances, fetchShape_fn).call(this, {
757
+ fetchUrl,
758
+ requestAbortController,
759
+ headers: requestHeaders,
760
+ resumingFromPause
761
+ });
762
+ } catch (e) {
763
+ if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && requestAbortController.signal.aborted && requestAbortController.signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
764
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
765
+ }
766
+ if (e instanceof FetchBackoffAbortError) {
767
+ if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM) {
768
+ __privateSet(this, _state, `paused`);
769
+ }
770
+ return;
771
+ }
772
+ if (!(e instanceof FetchError)) throw e;
773
+ if (e.status == 409) {
774
+ const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
775
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
776
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
777
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
778
+ } else {
779
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
780
+ throw e;
781
+ }
782
+ } finally {
783
+ if (abortListener && signal) {
784
+ signal.removeEventListener(`abort`, abortListener);
785
+ }
786
+ __privateSet(this, _requestAbortController, void 0);
787
+ }
788
+ (_b = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _b.call(this);
789
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
790
+ });
791
+ };
792
+ constructUrl_fn = function(url, resumingFromPause) {
793
+ return __async(this, null, function* () {
732
794
  const [requestHeaders, params] = yield Promise.all([
733
795
  resolveHeaders(this.options.headers),
734
796
  this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
@@ -769,51 +831,32 @@ requestShape_fn = function() {
769
831
  fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
770
832
  }
771
833
  fetchUrl.searchParams.sort();
834
+ return {
835
+ fetchUrl,
836
+ requestHeaders
837
+ };
838
+ });
839
+ };
840
+ createAbortListener_fn = function(signal) {
841
+ return __async(this, null, function* () {
842
+ var _a;
772
843
  __privateSet(this, _requestAbortController, new AbortController());
773
- let abortListener;
774
844
  if (signal) {
775
- abortListener = () => {
845
+ const abortListener = () => {
776
846
  var _a2;
777
847
  (_a2 = __privateGet(this, _requestAbortController)) == null ? void 0 : _a2.abort(signal.reason);
778
848
  };
779
849
  signal.addEventListener(`abort`, abortListener, { once: true });
780
850
  if (signal.aborted) {
781
- (_b = __privateGet(this, _requestAbortController)) == null ? void 0 : _b.abort(signal.reason);
782
- }
783
- }
784
- let response;
785
- try {
786
- response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
787
- signal: __privateGet(this, _requestAbortController).signal,
788
- headers: requestHeaders
789
- });
790
- __privateSet(this, _connected, true);
791
- } catch (e) {
792
- if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && __privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
793
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
794
- }
795
- if (e instanceof FetchBackoffAbortError) {
796
- if (__privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === PAUSE_STREAM) {
797
- __privateSet(this, _state, `paused`);
798
- }
799
- return;
800
- }
801
- if (!(e instanceof FetchError)) throw e;
802
- if (e.status == 409) {
803
- const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
804
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
805
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
806
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
807
- } else {
808
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
809
- throw e;
851
+ (_a = __privateGet(this, _requestAbortController)) == null ? void 0 : _a.abort(signal.reason);
810
852
  }
811
- } finally {
812
- if (abortListener && signal) {
813
- signal.removeEventListener(`abort`, abortListener);
814
- }
815
- __privateSet(this, _requestAbortController, void 0);
853
+ return abortListener;
816
854
  }
855
+ });
856
+ };
857
+ onInitialResponse_fn = function(response) {
858
+ return __async(this, null, function* () {
859
+ var _a;
817
860
  const { headers, status } = response;
818
861
  const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
819
862
  if (shapeHandle) {
@@ -831,22 +874,93 @@ requestShape_fn = function() {
831
874
  const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
832
875
  return schemaHeader ? JSON.parse(schemaHeader) : {};
833
876
  };
834
- __privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
877
+ __privateSet(this, _schema, (_a = __privateGet(this, _schema)) != null ? _a : getSchema());
835
878
  if (status === 204) {
836
879
  __privateSet(this, _lastSyncedAt, Date.now());
837
880
  }
838
- const messages = (yield response.text()) || `[]`;
839
- const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
881
+ });
882
+ };
883
+ onMessages_fn = function(batch, isSseMessage = false) {
884
+ return __async(this, null, function* () {
840
885
  if (batch.length > 0) {
841
886
  const lastMessage = batch[batch.length - 1];
842
887
  if (isUpToDateMessage(lastMessage)) {
888
+ if (isSseMessage) {
889
+ const offset = getOffset(lastMessage);
890
+ if (offset) {
891
+ __privateSet(this, _lastOffset, offset);
892
+ }
893
+ }
843
894
  __privateSet(this, _lastSyncedAt, Date.now());
844
895
  __privateSet(this, _isUpToDate, true);
845
896
  }
846
897
  yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
847
898
  }
848
- (_d = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _d.call(this);
849
- return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
899
+ });
900
+ };
901
+ fetchShape_fn = function(opts) {
902
+ return __async(this, null, function* () {
903
+ if (__privateGet(this, _isUpToDate) && this.options.experimentalLiveSse && !__privateGet(this, _isRefreshing) && !opts.resumingFromPause) {
904
+ opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
905
+ return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
906
+ }
907
+ return __privateMethod(this, _ShapeStream_instances, requestShapeLongPoll_fn).call(this, opts);
908
+ });
909
+ };
910
+ requestShapeLongPoll_fn = function(opts) {
911
+ return __async(this, null, function* () {
912
+ const { fetchUrl, requestAbortController, headers } = opts;
913
+ const response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
914
+ signal: requestAbortController.signal,
915
+ headers
916
+ });
917
+ __privateSet(this, _connected, true);
918
+ yield __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
919
+ const schema = __privateGet(this, _schema);
920
+ const res = yield response.text();
921
+ const messages = res || `[]`;
922
+ const batch = __privateGet(this, _messageParser).parse(messages, schema);
923
+ yield __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, batch);
924
+ });
925
+ };
926
+ requestShapeSSE_fn = function(opts) {
927
+ return __async(this, null, function* () {
928
+ const { fetchUrl, requestAbortController, headers } = opts;
929
+ const fetch2 = __privateGet(this, _sseFetchClient);
930
+ try {
931
+ let buffer = [];
932
+ yield fetchEventSource(fetchUrl.toString(), {
933
+ headers,
934
+ fetch: fetch2,
935
+ onopen: (response) => __async(this, null, function* () {
936
+ __privateSet(this, _connected, true);
937
+ yield __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
938
+ }),
939
+ onmessage: (event) => {
940
+ if (event.data) {
941
+ const schema = __privateGet(this, _schema);
942
+ const message = __privateGet(this, _messageParser).parse(
943
+ event.data,
944
+ schema
945
+ );
946
+ buffer.push(message);
947
+ if (isUpToDateMessage(message)) {
948
+ __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, buffer, true);
949
+ buffer = [];
950
+ }
951
+ }
952
+ },
953
+ onerror: (error) => {
954
+ throw error;
955
+ },
956
+ signal: requestAbortController.signal
957
+ });
958
+ } catch (error) {
959
+ if (requestAbortController.signal.aborted) {
960
+ throw new FetchBackoffAbortError();
961
+ }
962
+ throw error;
963
+ }
850
964
  });
851
965
  };
852
966
  pause_fn = function() {
@@ -880,17 +994,20 @@ nextTick_fn = function() {
880
994
  };
881
995
  publish_fn = function(messages) {
882
996
  return __async(this, null, function* () {
883
- yield Promise.all(
884
- Array.from(__privateGet(this, _subscribers).values()).map((_0) => __async(this, [_0], function* ([callback, __]) {
885
- try {
886
- yield callback(messages);
887
- } catch (err) {
888
- queueMicrotask(() => {
889
- throw err;
890
- });
891
- }
892
- }))
893
- );
997
+ __privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
998
+ () => Promise.all(
999
+ Array.from(__privateGet(this, _subscribers).values()).map((_0) => __async(this, [_0], function* ([callback, __]) {
1000
+ try {
1001
+ yield callback(messages);
1002
+ } catch (err) {
1003
+ queueMicrotask(() => {
1004
+ throw err;
1005
+ });
1006
+ }
1007
+ }))
1008
+ )
1009
+ ));
1010
+ return __privateGet(this, _messageChain);
894
1011
  });
895
1012
  };
896
1013
  sendErrorToSubscribers_fn = function(error) {