@electric-sql/client 1.1.3 → 1.1.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.d.ts CHANGED
@@ -368,16 +368,14 @@ interface ShapeStreamInterface<T extends Row<unknown> = Row> {
368
368
  error?: unknown;
369
369
  mode: LogMode;
370
370
  forceDisconnectAndRefresh(): Promise<void>;
371
- requestSnapshot(params: {
372
- where?: string;
373
- params?: Record<string, string>;
374
- limit: number;
375
- offset?: number;
376
- orderBy: string;
377
- }): Promise<{
371
+ requestSnapshot(params: SubsetParams): Promise<{
378
372
  metadata: SnapshotMetadata;
379
373
  data: Array<Message<T>>;
380
374
  }>;
375
+ fetchSnapshot(opts: SubsetParams): Promise<{
376
+ metadata: SnapshotMetadata;
377
+ data: Array<ChangeMessage<T>>;
378
+ }>;
381
379
  }
382
380
  /**
383
381
  * Reads updates to a shape from Electric using HTTP requests and long polling or
@@ -451,7 +449,7 @@ declare class ShapeStream<T extends Row<unknown> = Row> implements ShapeStreamIn
451
449
  */
452
450
  forceDisconnectAndRefresh(): Promise<void>;
453
451
  /**
454
- * Request a snapshot for subset of data.
452
+ * Request a snapshot for subset of data and inject it into the subscribed data stream.
455
453
  *
456
454
  * Only available when mode is `changes_only`.
457
455
  * Returns the insertion point & the data, but more importantly injects the data
@@ -468,6 +466,17 @@ declare class ShapeStream<T extends Row<unknown> = Row> implements ShapeStreamIn
468
466
  metadata: SnapshotMetadata;
469
467
  data: Array<ChangeMessage<T>>;
470
468
  }>;
469
+ /**
470
+ * Fetch a snapshot for subset of data.
471
+ * Returns the metadata and the data, but does not inject it into the subscribed data stream.
472
+ *
473
+ * @param opts - The options for the snapshot request.
474
+ * @returns The metadata and the data for the snapshot.
475
+ */
476
+ fetchSnapshot(opts: SubsetParams): Promise<{
477
+ metadata: SnapshotMetadata;
478
+ data: Array<ChangeMessage<T>>;
479
+ }>;
471
480
  }
472
481
 
473
482
  type ShapeData<T extends Row<unknown> = Row> = Map<string, T>;
@@ -203,15 +203,37 @@ var MessageParser = class {
203
203
  parse(messages, schema) {
204
204
  return JSON.parse(messages, (key, value) => {
205
205
  if ((key === `value` || key === `old_value`) && typeof value === `object` && value !== null) {
206
- const row = value;
207
- Object.keys(row).forEach((key2) => {
208
- row[key2] = this.parseRow(key2, row[key2], schema);
209
- });
210
- if (this.transformer) value = this.transformer(value);
206
+ return this.transformMessageValue(value, schema);
211
207
  }
212
208
  return value;
213
209
  });
214
210
  }
211
+ /**
212
+ * Parse an array of ChangeMessages from a snapshot response.
213
+ * Applies type parsing and transformations to the value and old_value properties.
214
+ */
215
+ parseSnapshotData(messages, schema) {
216
+ return messages.map((message) => {
217
+ const msg = message;
218
+ if (msg.value && typeof msg.value === `object` && msg.value !== null) {
219
+ msg.value = this.transformMessageValue(msg.value, schema);
220
+ }
221
+ if (msg.old_value && typeof msg.old_value === `object` && msg.old_value !== null) {
222
+ msg.old_value = this.transformMessageValue(msg.old_value, schema);
223
+ }
224
+ return msg;
225
+ });
226
+ }
227
+ /**
228
+ * Transform a message value or old_value object by parsing its columns.
229
+ */
230
+ transformMessageValue(value, schema) {
231
+ const row = value;
232
+ Object.keys(row).forEach((key) => {
233
+ row[key] = this.parseRow(key, row[key], schema);
234
+ });
235
+ return this.transformer ? this.transformer(row) : row;
236
+ }
215
237
  // Parses the message values using the provided parser based on the schema information
216
238
  parseRow(key, value, schema) {
217
239
  var _b;
@@ -751,9 +773,8 @@ function canonicalShapeKey(url) {
751
773
  cleanUrl.searchParams.sort();
752
774
  return cleanUrl.toString();
753
775
  }
754
- var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _state, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _isMidStream, _connected, _shapeHandle, _mode, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _activeSnapshotRequests, _midStreamPromise, _midStreamPromiseResolver, _lastSseConnectionStartTime, _minSseConnectionDuration, _consecutiveShortSseConnections, _maxShortSseConnections, _sseFallbackToLongPolling, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _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, waitForStreamEnd_fn, publish_fn, sendErrorToSubscribers_fn, subscribeToVisibilityChanges_fn, reset_fn, fetchSnapshot_fn;
776
+ var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _state, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _isMidStream, _connected, _shapeHandle, _mode, _schema, _onError, _requestAbortController, _isRefreshing, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _activeSnapshotRequests, _midStreamPromise, _midStreamPromiseResolver, _lastSseConnectionStartTime, _minSseConnectionDuration, _consecutiveShortSseConnections, _maxShortSseConnections, _sseFallbackToLongPolling, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _unsubscribeFromVisibilityChanges, _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, waitForStreamEnd_fn, publish_fn, sendErrorToSubscribers_fn, subscribeToVisibilityChanges_fn, reset_fn;
755
777
  var ShapeStream = class {
756
- // Maximum delay cap (ms)
757
778
  constructor(options) {
758
779
  __privateAdd(this, _ShapeStream_instances);
759
780
  __privateAdd(this, _error, null);
@@ -797,6 +818,8 @@ var ShapeStream = class {
797
818
  __privateAdd(this, _sseBackoffBaseDelay, 100);
798
819
  // Base delay for exponential backoff (ms)
799
820
  __privateAdd(this, _sseBackoffMaxDelay, 5e3);
821
+ // Maximum delay cap (ms)
822
+ __privateAdd(this, _unsubscribeFromVisibilityChanges);
800
823
  var _a, _b, _c, _d;
801
824
  this.options = __spreadValues({ subscribe: true }, options);
802
825
  validateOptions(this.options);
@@ -852,7 +875,9 @@ var ShapeStream = class {
852
875
  };
853
876
  }
854
877
  unsubscribeAll() {
878
+ var _a;
855
879
  __privateGet(this, _subscribers).clear();
880
+ (_a = __privateGet(this, _unsubscribeFromVisibilityChanges)) == null ? void 0 : _a.call(this);
856
881
  }
857
882
  /** Unix time at which we last synced. Undefined when `isLoading` is true. */
858
883
  lastSyncedAt() {
@@ -893,7 +918,7 @@ var ShapeStream = class {
893
918
  __privateSet(this, _isRefreshing, false);
894
919
  }
895
920
  /**
896
- * Request a snapshot for subset of data.
921
+ * Request a snapshot for subset of data and inject it into the subscribed data stream.
897
922
  *
898
923
  * Only available when mode is `changes_only`.
899
924
  * Returns the insertion point & the data, but more importantly injects the data
@@ -919,8 +944,7 @@ var ShapeStream = class {
919
944
  if (__privateGet(this, _activeSnapshotRequests) === 1) {
920
945
  __privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
921
946
  }
922
- const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
923
- const { metadata, data } = await __privateMethod(this, _ShapeStream_instances, fetchSnapshot_fn).call(this, fetchUrl, requestHeaders);
947
+ const { metadata, data } = await this.fetchSnapshot(opts);
924
948
  const dataWithEndBoundary = data.concat([
925
949
  { headers: __spreadValues({ control: `snapshot-end` }, metadata) }
926
950
  ]);
@@ -940,6 +964,42 @@ var ShapeStream = class {
940
964
  }
941
965
  }
942
966
  }
967
+ /**
968
+ * Fetch a snapshot for subset of data.
969
+ * Returns the metadata and the data, but does not inject it into the subscribed data stream.
970
+ *
971
+ * @param opts - The options for the snapshot request.
972
+ * @returns The metadata and the data for the snapshot.
973
+ */
974
+ async fetchSnapshot(opts) {
975
+ var _a;
976
+ const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
977
+ const response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
978
+ headers: requestHeaders
979
+ });
980
+ if (!response.ok) {
981
+ throw new FetchError(
982
+ response.status,
983
+ void 0,
984
+ void 0,
985
+ Object.fromEntries([...response.headers.entries()]),
986
+ fetchUrl.toString()
987
+ );
988
+ }
989
+ const schema = (_a = __privateGet(this, _schema)) != null ? _a : getSchemaFromHeaders(response.headers, {
990
+ required: true,
991
+ url: fetchUrl.toString()
992
+ });
993
+ const { metadata, data: rawData } = await response.json();
994
+ const data = __privateGet(this, _messageParser).parseSnapshotData(
995
+ rawData,
996
+ schema
997
+ );
998
+ return {
999
+ metadata,
1000
+ data
1001
+ };
1002
+ }
943
1003
  };
944
1004
  _error = new WeakMap();
945
1005
  _fetchClient2 = new WeakMap();
@@ -975,6 +1035,7 @@ _maxShortSseConnections = new WeakMap();
975
1035
  _sseFallbackToLongPolling = new WeakMap();
976
1036
  _sseBackoffBaseDelay = new WeakMap();
977
1037
  _sseBackoffMaxDelay = new WeakMap();
1038
+ _unsubscribeFromVisibilityChanges = new WeakMap();
978
1039
  _ShapeStream_instances = new WeakSet();
979
1040
  start_fn = async function() {
980
1041
  var _a, _b, _c, _d, _e;
@@ -1041,7 +1102,8 @@ requestShape_fn = async function() {
1041
1102
  return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
1042
1103
  }
1043
1104
  if (e instanceof FetchBackoffAbortError) {
1044
- if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM) {
1105
+ const currentState = __privateGet(this, _state);
1106
+ if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM && currentState === `pause-requested`) {
1045
1107
  __privateSet(this, _state, `paused`);
1046
1108
  }
1047
1109
  return;
@@ -1107,7 +1169,8 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
1107
1169
  }
1108
1170
  fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
1109
1171
  fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, __privateGet(this, _mode));
1110
- if (__privateGet(this, _isUpToDate)) {
1172
+ const isSnapshotRequest = subsetParams !== void 0;
1173
+ if (__privateGet(this, _isUpToDate) && !isSnapshotRequest) {
1111
1174
  if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
1112
1175
  fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
1113
1176
  }
@@ -1160,11 +1223,7 @@ onInitialResponse_fn = async function(response) {
1160
1223
  if (liveCacheBuster) {
1161
1224
  __privateSet(this, _liveCacheBuster, liveCacheBuster);
1162
1225
  }
1163
- const getSchema = () => {
1164
- const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
1165
- return schemaHeader ? JSON.parse(schemaHeader) : {};
1166
- };
1167
- __privateSet(this, _schema, (_a = __privateGet(this, _schema)) != null ? _a : getSchema());
1226
+ __privateSet(this, _schema, (_a = __privateGet(this, _schema)) != null ? _a : getSchemaFromHeaders(headers));
1168
1227
  if (status === 204) {
1169
1228
  __privateSet(this, _lastSyncedAt, Date.now());
1170
1229
  }
@@ -1287,7 +1346,10 @@ pause_fn = function() {
1287
1346
  }
1288
1347
  };
1289
1348
  resume_fn = function() {
1290
- if (__privateGet(this, _started) && __privateGet(this, _state) === `paused`) {
1349
+ if (__privateGet(this, _started) && (__privateGet(this, _state) === `paused` || __privateGet(this, _state) === `pause-requested`)) {
1350
+ if (__privateGet(this, _state) === `pause-requested`) {
1351
+ __privateSet(this, _state, `active`);
1352
+ }
1291
1353
  __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
1292
1354
  }
1293
1355
  };
@@ -1353,6 +1415,9 @@ subscribeToVisibilityChanges_fn = function() {
1353
1415
  }
1354
1416
  };
1355
1417
  document.addEventListener(`visibilitychange`, visibilityHandler);
1418
+ __privateSet(this, _unsubscribeFromVisibilityChanges, () => {
1419
+ document.removeEventListener(`visibilitychange`, visibilityHandler);
1420
+ });
1356
1421
  }
1357
1422
  };
1358
1423
  /**
@@ -1371,31 +1436,20 @@ reset_fn = function(handle) {
1371
1436
  __privateSet(this, _consecutiveShortSseConnections, 0);
1372
1437
  __privateSet(this, _sseFallbackToLongPolling, false);
1373
1438
  };
1374
- fetchSnapshot_fn = async function(url, headers) {
1375
- const response = await __privateGet(this, _fetchClient2).call(this, url.toString(), { headers });
1376
- if (!response.ok) {
1377
- throw new FetchError(
1378
- response.status,
1379
- void 0,
1380
- void 0,
1381
- Object.fromEntries([...response.headers.entries()]),
1382
- url.toString()
1383
- );
1384
- }
1385
- const { metadata, data } = await response.json();
1386
- const batch = __privateGet(this, _messageParser).parse(
1387
- JSON.stringify(data),
1388
- __privateGet(this, _schema)
1389
- );
1390
- return {
1391
- metadata,
1392
- data: batch
1393
- };
1394
- };
1395
1439
  ShapeStream.Replica = {
1396
1440
  FULL: `full`,
1397
1441
  DEFAULT: `default`
1398
1442
  };
1443
+ function getSchemaFromHeaders(headers, options) {
1444
+ const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
1445
+ if (!schemaHeader) {
1446
+ if ((options == null ? void 0 : options.required) && (options == null ? void 0 : options.url)) {
1447
+ throw new MissingHeadersError(options.url, [SHAPE_SCHEMA_HEADER]);
1448
+ }
1449
+ return {};
1450
+ }
1451
+ return JSON.parse(schemaHeader);
1452
+ }
1399
1453
  function validateParams(params) {
1400
1454
  if (!params) return;
1401
1455
  const reservedParams = Object.keys(params).filter(