@electric-sql/client 1.0.2 → 1.0.4

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
@@ -284,6 +284,7 @@ var WHERE_QUERY_PARAM = `where`;
284
284
  var REPLICA_PARAM = `replica`;
285
285
  var WHERE_PARAMS_PARAM = `params`;
286
286
  var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
287
+ var PAUSE_STREAM = `pause-stream`;
287
288
 
288
289
  // src/fetch.ts
289
290
  var HTTP_RETRY_STATUS_CODES = [429];
@@ -329,12 +330,15 @@ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
329
330
  }
330
331
  });
331
332
  }
333
+ var NO_BODY_STATUS_CODES = [201, 204, 205];
332
334
  function createFetchWithConsumedMessages(fetchClient) {
333
335
  return (...args) => __async(this, null, function* () {
334
336
  const url = args[0];
335
337
  const res = yield fetchClient(...args);
336
338
  try {
337
- if (res.body === null) return res;
339
+ if (res.status < 200 || NO_BODY_STATUS_CODES.includes(res.status)) {
340
+ return res;
341
+ }
338
342
  const text = yield res.text();
339
343
  return new Response(text, res);
340
344
  } catch (err) {
@@ -549,7 +553,7 @@ function resolveHeaders(headers) {
549
553
  return Object.fromEntries(resolvedEntries);
550
554
  });
551
555
  }
552
- var _error, _fetchClient2, _messageParser, _subscribers, _started, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _ShapeStream_instances, start_fn, nextTick_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
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;
553
557
  var ShapeStream = class {
554
558
  constructor(options) {
555
559
  __privateAdd(this, _ShapeStream_instances);
@@ -558,6 +562,7 @@ var ShapeStream = class {
558
562
  __privateAdd(this, _messageParser);
559
563
  __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
560
564
  __privateAdd(this, _started, false);
565
+ __privateAdd(this, _state, `active`);
561
566
  __privateAdd(this, _lastOffset);
562
567
  __privateAdd(this, _liveCacheBuster);
563
568
  // Seconds since our Electric Epoch 😎
@@ -594,6 +599,7 @@ var ShapeStream = class {
594
599
  createFetchWithChunkBuffer(fetchWithBackoffClient)
595
600
  )
596
601
  ));
602
+ __privateMethod(this, _ShapeStream_instances, subscribeToVisibilityChanges_fn).call(this);
597
603
  }
598
604
  get shapeHandle() {
599
605
  return __privateGet(this, _shapeHandle);
@@ -639,6 +645,9 @@ var ShapeStream = class {
639
645
  hasStarted() {
640
646
  return __privateGet(this, _started);
641
647
  }
648
+ isPaused() {
649
+ return __privateGet(this, _state) === `paused`;
650
+ }
642
651
  /**
643
652
  * Refreshes the shape stream.
644
653
  * This preemptively aborts any ongoing long poll and reconnects without
@@ -662,6 +671,7 @@ _fetchClient2 = new WeakMap();
662
671
  _messageParser = new WeakMap();
663
672
  _subscribers = new WeakMap();
664
673
  _started = new WeakMap();
674
+ _state = new WeakMap();
665
675
  _lastOffset = new WeakMap();
666
676
  _liveCacheBuster = new WeakMap();
667
677
  _lastSyncedAt = new WeakMap();
@@ -678,128 +688,10 @@ _tickPromiseRejecter = new WeakMap();
678
688
  _ShapeStream_instances = new WeakSet();
679
689
  start_fn = function() {
680
690
  return __async(this, null, function* () {
681
- var _a, _b, _c, _d, _e;
682
- if (__privateGet(this, _started)) throw new Error(`Cannot start stream twice`);
691
+ var _a;
683
692
  __privateSet(this, _started, true);
684
693
  try {
685
- while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
686
- const { url, signal } = this.options;
687
- const [requestHeaders, params] = yield Promise.all([
688
- resolveHeaders(this.options.headers),
689
- this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
690
- ]);
691
- if (params) {
692
- validateParams(params);
693
- }
694
- const fetchUrl = new URL(url);
695
- if (params) {
696
- if (params.table)
697
- setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table);
698
- if (params.where)
699
- setQueryParam(fetchUrl, WHERE_QUERY_PARAM, params.where);
700
- if (params.columns)
701
- setQueryParam(fetchUrl, COLUMNS_QUERY_PARAM, params.columns);
702
- if (params.replica)
703
- setQueryParam(fetchUrl, REPLICA_PARAM, params.replica);
704
- if (params.params)
705
- setQueryParam(fetchUrl, WHERE_PARAMS_PARAM, params.params);
706
- const customParams = __spreadValues({}, params);
707
- delete customParams.table;
708
- delete customParams.where;
709
- delete customParams.columns;
710
- delete customParams.replica;
711
- delete customParams.params;
712
- for (const [key, value] of Object.entries(customParams)) {
713
- setQueryParam(fetchUrl, key, value);
714
- }
715
- }
716
- fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
717
- if (__privateGet(this, _isUpToDate)) {
718
- if (!__privateGet(this, _isRefreshing)) {
719
- fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
720
- }
721
- fetchUrl.searchParams.set(
722
- LIVE_CACHE_BUSTER_QUERY_PARAM,
723
- __privateGet(this, _liveCacheBuster)
724
- );
725
- }
726
- if (__privateGet(this, _shapeHandle)) {
727
- fetchUrl.searchParams.set(
728
- SHAPE_HANDLE_QUERY_PARAM,
729
- __privateGet(this, _shapeHandle)
730
- );
731
- }
732
- fetchUrl.searchParams.sort();
733
- __privateSet(this, _requestAbortController, new AbortController());
734
- let abortListener;
735
- if (signal) {
736
- abortListener = () => {
737
- var _a2;
738
- (_a2 = __privateGet(this, _requestAbortController)) == null ? void 0 : _a2.abort(signal.reason);
739
- };
740
- signal.addEventListener(`abort`, abortListener, { once: true });
741
- if (signal.aborted) {
742
- (_b = __privateGet(this, _requestAbortController)) == null ? void 0 : _b.abort(signal.reason);
743
- }
744
- }
745
- let response;
746
- try {
747
- response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
748
- signal: __privateGet(this, _requestAbortController).signal,
749
- headers: requestHeaders
750
- });
751
- __privateSet(this, _connected, true);
752
- } catch (e) {
753
- if ((e instanceof FetchError || e instanceof FetchBackoffAbortError) && __privateGet(this, _requestAbortController).signal.aborted && __privateGet(this, _requestAbortController).signal.reason === FORCE_DISCONNECT_AND_REFRESH) {
754
- continue;
755
- }
756
- if (e instanceof FetchBackoffAbortError) break;
757
- if (!(e instanceof FetchError)) throw e;
758
- if (e.status == 409) {
759
- const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
760
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
761
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
762
- continue;
763
- } else {
764
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
765
- throw e;
766
- }
767
- } finally {
768
- if (abortListener && signal) {
769
- signal.removeEventListener(`abort`, abortListener);
770
- }
771
- __privateSet(this, _requestAbortController, void 0);
772
- }
773
- const { headers } = response;
774
- const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
775
- if (shapeHandle) {
776
- __privateSet(this, _shapeHandle, shapeHandle);
777
- }
778
- const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
779
- if (lastOffset) {
780
- __privateSet(this, _lastOffset, lastOffset);
781
- }
782
- const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
783
- if (liveCacheBuster) {
784
- __privateSet(this, _liveCacheBuster, liveCacheBuster);
785
- }
786
- const getSchema = () => {
787
- const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
788
- return schemaHeader ? JSON.parse(schemaHeader) : {};
789
- };
790
- __privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
791
- const messages = yield response.text();
792
- const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
793
- if (batch.length > 0) {
794
- const lastMessage = batch[batch.length - 1];
795
- if (isUpToDateMessage(lastMessage)) {
796
- __privateSet(this, _lastSyncedAt, Date.now());
797
- __privateSet(this, _isUpToDate, true);
798
- }
799
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
800
- }
801
- (_d = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _d.call(this);
802
- }
694
+ yield __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
803
695
  } catch (err) {
804
696
  __privateSet(this, _error, err);
805
697
  if (__privateGet(this, _onError)) {
@@ -820,10 +712,155 @@ start_fn = function() {
820
712
  throw err;
821
713
  } finally {
822
714
  __privateSet(this, _connected, false);
823
- (_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
715
+ (_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
824
716
  }
825
717
  });
826
718
  };
719
+ requestShape_fn = function() {
720
+ return __async(this, null, function* () {
721
+ var _a, _b, _c, _d;
722
+ if (__privateGet(this, _state) === `pause-requested`) {
723
+ __privateSet(this, _state, `paused`);
724
+ return;
725
+ }
726
+ if (!this.options.subscribe && (((_a = this.options.signal) == null ? void 0 : _a.aborted) || __privateGet(this, _isUpToDate))) {
727
+ return;
728
+ }
729
+ const resumingFromPause = __privateGet(this, _state) === `paused`;
730
+ __privateSet(this, _state, `active`);
731
+ const { url, signal } = this.options;
732
+ const [requestHeaders, params] = yield Promise.all([
733
+ resolveHeaders(this.options.headers),
734
+ this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
735
+ ]);
736
+ if (params) {
737
+ validateParams(params);
738
+ }
739
+ const fetchUrl = new URL(url);
740
+ if (params) {
741
+ if (params.table) setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table);
742
+ if (params.where) setQueryParam(fetchUrl, WHERE_QUERY_PARAM, params.where);
743
+ if (params.columns)
744
+ setQueryParam(fetchUrl, COLUMNS_QUERY_PARAM, params.columns);
745
+ if (params.replica) setQueryParam(fetchUrl, REPLICA_PARAM, params.replica);
746
+ if (params.params)
747
+ setQueryParam(fetchUrl, WHERE_PARAMS_PARAM, params.params);
748
+ const customParams = __spreadValues({}, params);
749
+ delete customParams.table;
750
+ delete customParams.where;
751
+ delete customParams.columns;
752
+ delete customParams.replica;
753
+ delete customParams.params;
754
+ for (const [key, value] of Object.entries(customParams)) {
755
+ setQueryParam(fetchUrl, key, value);
756
+ }
757
+ }
758
+ fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
759
+ if (__privateGet(this, _isUpToDate)) {
760
+ if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
761
+ fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
762
+ }
763
+ fetchUrl.searchParams.set(
764
+ LIVE_CACHE_BUSTER_QUERY_PARAM,
765
+ __privateGet(this, _liveCacheBuster)
766
+ );
767
+ }
768
+ if (__privateGet(this, _shapeHandle)) {
769
+ fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
770
+ }
771
+ fetchUrl.searchParams.sort();
772
+ __privateSet(this, _requestAbortController, new AbortController());
773
+ let abortListener;
774
+ if (signal) {
775
+ abortListener = () => {
776
+ var _a2;
777
+ (_a2 = __privateGet(this, _requestAbortController)) == null ? void 0 : _a2.abort(signal.reason);
778
+ };
779
+ signal.addEventListener(`abort`, abortListener, { once: true });
780
+ 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;
810
+ }
811
+ } finally {
812
+ if (abortListener && signal) {
813
+ signal.removeEventListener(`abort`, abortListener);
814
+ }
815
+ __privateSet(this, _requestAbortController, void 0);
816
+ }
817
+ const { headers, status } = response;
818
+ const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
819
+ if (shapeHandle) {
820
+ __privateSet(this, _shapeHandle, shapeHandle);
821
+ }
822
+ const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
823
+ if (lastOffset) {
824
+ __privateSet(this, _lastOffset, lastOffset);
825
+ }
826
+ const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
827
+ if (liveCacheBuster) {
828
+ __privateSet(this, _liveCacheBuster, liveCacheBuster);
829
+ }
830
+ const getSchema = () => {
831
+ const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
832
+ return schemaHeader ? JSON.parse(schemaHeader) : {};
833
+ };
834
+ __privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
835
+ if (status === 204) {
836
+ __privateSet(this, _lastSyncedAt, Date.now());
837
+ }
838
+ const messages = (yield response.text()) || `[]`;
839
+ const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
840
+ if (batch.length > 0) {
841
+ const lastMessage = batch[batch.length - 1];
842
+ if (isUpToDateMessage(lastMessage)) {
843
+ __privateSet(this, _lastSyncedAt, Date.now());
844
+ __privateSet(this, _isUpToDate, true);
845
+ }
846
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
847
+ }
848
+ (_d = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _d.call(this);
849
+ return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
850
+ });
851
+ };
852
+ pause_fn = function() {
853
+ var _a;
854
+ if (__privateGet(this, _started) && __privateGet(this, _state) === `active`) {
855
+ __privateSet(this, _state, `pause-requested`);
856
+ (_a = __privateGet(this, _requestAbortController)) == null ? void 0 : _a.abort(PAUSE_STREAM);
857
+ }
858
+ };
859
+ resume_fn = function() {
860
+ if (__privateGet(this, _started) && __privateGet(this, _state) === `paused`) {
861
+ __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
862
+ }
863
+ };
827
864
  nextTick_fn = function() {
828
865
  return __async(this, null, function* () {
829
866
  if (__privateGet(this, _tickPromise)) {
@@ -861,6 +898,18 @@ sendErrorToSubscribers_fn = function(error) {
861
898
  errorFn == null ? void 0 : errorFn(error);
862
899
  });
863
900
  };
901
+ subscribeToVisibilityChanges_fn = function() {
902
+ if (typeof document === `object` && typeof document.hidden === `boolean` && typeof document.addEventListener === `function`) {
903
+ const visibilityHandler = () => {
904
+ if (document.hidden) {
905
+ __privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
906
+ } else {
907
+ __privateMethod(this, _ShapeStream_instances, resume_fn).call(this);
908
+ }
909
+ };
910
+ document.addEventListener(`visibilitychange`, visibilityHandler);
911
+ }
912
+ };
864
913
  /**
865
914
  * Resets the state of the stream, optionally with a provided
866
915
  * shape handle