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