@electric-sql/client 1.0.4 → 1.0.5

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