@electric-sql/client 1.0.10 → 1.0.12

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.
@@ -53,6 +53,14 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
53
53
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
54
54
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
55
55
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
56
+ var __privateWrapper = (obj, member, setter, getter) => ({
57
+ set _(value) {
58
+ __privateSet(obj, member, value, setter);
59
+ },
60
+ get _() {
61
+ return __privateGet(obj, member, getter);
62
+ }
63
+ });
56
64
  var __async = (__this, __arguments, generator) => {
57
65
  return new Promise((resolve, reject) => {
58
66
  var fulfilled = (value) => {
@@ -84,6 +92,7 @@ __export(src_exports, {
84
92
  ShapeStream: () => ShapeStream,
85
93
  isChangeMessage: () => isChangeMessage,
86
94
  isControlMessage: () => isControlMessage,
95
+ isVisibleInSnapshot: () => isVisibleInSnapshot,
87
96
  resolveValue: () => resolveValue
88
97
  });
89
98
  module.exports = __toCommonJS(src_exports);
@@ -308,6 +317,13 @@ function getOffset(message) {
308
317
  }
309
318
  return `${lsn}_0`;
310
319
  }
320
+ function isVisibleInSnapshot(txid, snapshot) {
321
+ const xid = BigInt(txid);
322
+ const xmin = BigInt(snapshot.xmin);
323
+ const xmax = BigInt(snapshot.xmax);
324
+ const xip = snapshot.xip_list.map(BigInt);
325
+ return xid < xmin || xid < xmax && !xip.includes(xid);
326
+ }
311
327
 
312
328
  // src/constants.ts
313
329
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
@@ -328,12 +344,24 @@ var WHERE_PARAMS_PARAM = `params`;
328
344
  var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
329
345
  var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
330
346
  var PAUSE_STREAM = `pause-stream`;
347
+ var LOG_MODE_QUERY_PARAM = `log`;
348
+ var SUBSET_PARAM_WHERE = `subset__where`;
349
+ var SUBSET_PARAM_LIMIT = `subset__limit`;
350
+ var SUBSET_PARAM_OFFSET = `subset__offset`;
351
+ var SUBSET_PARAM_ORDER_BY = `subset__order_by`;
352
+ var SUBSET_PARAM_WHERE_PARAMS = `subset__params`;
331
353
  var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
332
354
  LIVE_QUERY_PARAM,
333
355
  SHAPE_HANDLE_QUERY_PARAM,
334
356
  OFFSET_QUERY_PARAM,
335
357
  LIVE_CACHE_BUSTER_QUERY_PARAM,
336
- EXPIRED_HANDLE_QUERY_PARAM
358
+ EXPIRED_HANDLE_QUERY_PARAM,
359
+ LOG_MODE_QUERY_PARAM,
360
+ SUBSET_PARAM_WHERE,
361
+ SUBSET_PARAM_LIMIT,
362
+ SUBSET_PARAM_OFFSET,
363
+ SUBSET_PARAM_ORDER_BY,
364
+ SUBSET_PARAM_WHERE_PARAMS
337
365
  ];
338
366
 
339
367
  // src/fetch.ts
@@ -444,10 +472,20 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
444
472
  const headers = response.headers;
445
473
  const missingHeaders = [];
446
474
  const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
447
- addMissingHeaders(requiredElectricResponseHeaders);
448
475
  const input = args[0];
449
476
  const urlString = input.toString();
450
477
  const url = new URL(urlString);
478
+ const isSnapshotRequest = [
479
+ SUBSET_PARAM_WHERE,
480
+ SUBSET_PARAM_WHERE_PARAMS,
481
+ SUBSET_PARAM_LIMIT,
482
+ SUBSET_PARAM_OFFSET,
483
+ SUBSET_PARAM_ORDER_BY
484
+ ].some((p) => url.searchParams.has(p));
485
+ if (isSnapshotRequest) {
486
+ return response;
487
+ }
488
+ addMissingHeaders(requiredElectricResponseHeaders);
451
489
  if (url.searchParams.get(LIVE_QUERY_PARAM) === `true`) {
452
490
  addMissingHeaders(requiredLiveResponseHeaders);
453
491
  }
@@ -615,6 +653,68 @@ var ExpiredShapesCache = class {
615
653
  };
616
654
  var expiredShapesCache = new ExpiredShapesCache();
617
655
 
656
+ // src/snapshot-tracker.ts
657
+ var SnapshotTracker = class {
658
+ constructor() {
659
+ this.activeSnapshots = /* @__PURE__ */ new Map();
660
+ this.xmaxSnapshots = /* @__PURE__ */ new Map();
661
+ this.snapshotsByDatabaseLsn = /* @__PURE__ */ new Map();
662
+ }
663
+ /**
664
+ * Add a new snapshot for tracking
665
+ */
666
+ addSnapshot(metadata, keys) {
667
+ var _a, _b, _c, _d;
668
+ this.activeSnapshots.set(metadata.snapshot_mark, {
669
+ xmin: BigInt(metadata.xmin),
670
+ xmax: BigInt(metadata.xmax),
671
+ xip_list: metadata.xip_list.map(BigInt),
672
+ keys
673
+ });
674
+ const xmaxSet = (_b = (_a = this.xmaxSnapshots.get(BigInt(metadata.xmax))) == null ? void 0 : _a.add(metadata.snapshot_mark)) != null ? _b : /* @__PURE__ */ new Set([metadata.snapshot_mark]);
675
+ this.xmaxSnapshots.set(BigInt(metadata.xmax), xmaxSet);
676
+ const databaseLsnSet = (_d = (_c = this.snapshotsByDatabaseLsn.get(BigInt(metadata.database_lsn))) == null ? void 0 : _c.add(metadata.snapshot_mark)) != null ? _d : /* @__PURE__ */ new Set([metadata.snapshot_mark]);
677
+ this.snapshotsByDatabaseLsn.set(
678
+ BigInt(metadata.database_lsn),
679
+ databaseLsnSet
680
+ );
681
+ }
682
+ /**
683
+ * Remove a snapshot from tracking
684
+ */
685
+ removeSnapshot(snapshotMark) {
686
+ this.activeSnapshots.delete(snapshotMark);
687
+ }
688
+ /**
689
+ * Check if a change message should be filtered because its already in an active snapshot
690
+ * Returns true if the message should be filtered out (not processed)
691
+ */
692
+ shouldRejectMessage(message) {
693
+ const txids = message.headers.txids || [];
694
+ if (txids.length === 0) return false;
695
+ const xid = Math.max(...txids);
696
+ for (const [xmax, snapshots] of this.xmaxSnapshots.entries()) {
697
+ if (xid >= xmax) {
698
+ for (const snapshot of snapshots) {
699
+ this.removeSnapshot(snapshot);
700
+ }
701
+ }
702
+ }
703
+ return [...this.activeSnapshots.values()].some(
704
+ (x) => x.keys.has(message.key) && isVisibleInSnapshot(xid, x)
705
+ );
706
+ }
707
+ lastSeenUpdate(newDatabaseLsn) {
708
+ for (const [dbLsn, snapshots] of this.snapshotsByDatabaseLsn.entries()) {
709
+ if (dbLsn <= newDatabaseLsn) {
710
+ for (const snapshot of snapshots) {
711
+ this.removeSnapshot(snapshot);
712
+ }
713
+ }
714
+ }
715
+ }
716
+ };
717
+
618
718
  // src/client.ts
619
719
  var RESERVED_PARAMS = /* @__PURE__ */ new Set([
620
720
  LIVE_CACHE_BUSTER_QUERY_PARAM,
@@ -670,9 +770,8 @@ function canonicalShapeKey(url) {
670
770
  cleanUrl.searchParams.sort();
671
771
  return cleanUrl.toString();
672
772
  }
673
- 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;
773
+ 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, _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;
674
774
  var ShapeStream = class {
675
- // promise chain for incoming messages
676
775
  constructor(options) {
677
776
  __privateAdd(this, _ShapeStream_instances);
678
777
  __privateAdd(this, _error, null);
@@ -688,8 +787,10 @@ var ShapeStream = class {
688
787
  __privateAdd(this, _lastSyncedAt);
689
788
  // unix time
690
789
  __privateAdd(this, _isUpToDate, false);
790
+ __privateAdd(this, _isMidStream, true);
691
791
  __privateAdd(this, _connected, false);
692
792
  __privateAdd(this, _shapeHandle);
793
+ __privateAdd(this, _mode);
693
794
  __privateAdd(this, _schema);
694
795
  __privateAdd(this, _onError);
695
796
  __privateAdd(this, _requestAbortController);
@@ -698,7 +799,13 @@ var ShapeStream = class {
698
799
  __privateAdd(this, _tickPromiseResolver);
699
800
  __privateAdd(this, _tickPromiseRejecter);
700
801
  __privateAdd(this, _messageChain, Promise.resolve([]));
701
- var _a, _b, _c;
802
+ // promise chain for incoming messages
803
+ __privateAdd(this, _snapshotTracker, new SnapshotTracker());
804
+ __privateAdd(this, _activeSnapshotRequests, 0);
805
+ // counter for concurrent snapshot requests
806
+ __privateAdd(this, _midStreamPromise);
807
+ __privateAdd(this, _midStreamPromiseResolver);
808
+ var _a, _b, _c, _d;
702
809
  this.options = __spreadValues({ subscribe: true }, options);
703
810
  validateOptions(this.options);
704
811
  __privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
@@ -709,8 +816,9 @@ var ShapeStream = class {
709
816
  options.transformer
710
817
  ));
711
818
  __privateSet(this, _onError, this.options.onError);
712
- const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
713
- const backOffOpts = __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
819
+ __privateSet(this, _mode, (_b = this.options.log) != null ? _b : `full`);
820
+ const baseFetchClient = (_c = options.fetchClient) != null ? _c : (...args) => fetch(...args);
821
+ const backOffOpts = __spreadProps(__spreadValues({}, (_d = options.backoffOptions) != null ? _d : BackoffDefaults), {
714
822
  onFailedAttempt: () => {
715
823
  var _a2, _b2;
716
824
  __privateSet(this, _connected, false);
@@ -739,6 +847,9 @@ var ShapeStream = class {
739
847
  get lastOffset() {
740
848
  return __privateGet(this, _lastOffset);
741
849
  }
850
+ get mode() {
851
+ return __privateGet(this, _mode);
852
+ }
742
853
  subscribe(callback, onError = () => {
743
854
  }) {
744
855
  const subscriptionId = Math.random();
@@ -791,6 +902,56 @@ var ShapeStream = class {
791
902
  __privateSet(this, _isRefreshing, false);
792
903
  });
793
904
  }
905
+ /**
906
+ * Request a snapshot for subset of data.
907
+ *
908
+ * Only available when mode is `changes_only`.
909
+ * Returns the insertion point & the data, but more importantly injects the data
910
+ * into the subscribed data stream. Returned value is unlikely to be useful for the caller,
911
+ * unless the caller has complicated additional logic.
912
+ *
913
+ * Data will be injected in a way that's also tracking further incoming changes, and it'll
914
+ * skip the ones that are already in the snapshot.
915
+ *
916
+ * @param opts - The options for the snapshot request.
917
+ * @returns The metadata and the data for the snapshot.
918
+ */
919
+ requestSnapshot(opts) {
920
+ return __async(this, null, function* () {
921
+ if (__privateGet(this, _mode) === `full`) {
922
+ throw new Error(
923
+ `Snapshot requests are not supported in ${__privateGet(this, _mode)} mode, as the consumer is guaranteed to observe all data`
924
+ );
925
+ }
926
+ if (!__privateGet(this, _started)) yield __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
927
+ yield __privateMethod(this, _ShapeStream_instances, waitForStreamEnd_fn).call(this);
928
+ __privateWrapper(this, _activeSnapshotRequests)._++;
929
+ try {
930
+ if (__privateGet(this, _activeSnapshotRequests) === 1) {
931
+ __privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
932
+ }
933
+ const { fetchUrl, requestHeaders } = yield __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
934
+ const { metadata, data } = yield __privateMethod(this, _ShapeStream_instances, fetchSnapshot_fn).call(this, fetchUrl, requestHeaders);
935
+ const dataWithEndBoundary = data.concat([
936
+ { headers: __spreadValues({ control: `snapshot-end` }, metadata) }
937
+ ]);
938
+ __privateGet(this, _snapshotTracker).addSnapshot(
939
+ metadata,
940
+ new Set(data.map((message) => message.key))
941
+ );
942
+ __privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, dataWithEndBoundary, false);
943
+ return {
944
+ metadata,
945
+ data
946
+ };
947
+ } finally {
948
+ __privateWrapper(this, _activeSnapshotRequests)._--;
949
+ if (__privateGet(this, _activeSnapshotRequests) === 0) {
950
+ __privateMethod(this, _ShapeStream_instances, resume_fn).call(this);
951
+ }
952
+ }
953
+ });
954
+ }
794
955
  };
795
956
  _error = new WeakMap();
796
957
  _fetchClient2 = new WeakMap();
@@ -803,8 +964,10 @@ _lastOffset = new WeakMap();
803
964
  _liveCacheBuster = new WeakMap();
804
965
  _lastSyncedAt = new WeakMap();
805
966
  _isUpToDate = new WeakMap();
967
+ _isMidStream = new WeakMap();
806
968
  _connected = new WeakMap();
807
969
  _shapeHandle = new WeakMap();
970
+ _mode = new WeakMap();
808
971
  _schema = new WeakMap();
809
972
  _onError = new WeakMap();
810
973
  _requestAbortController = new WeakMap();
@@ -813,6 +976,10 @@ _tickPromise = new WeakMap();
813
976
  _tickPromiseResolver = new WeakMap();
814
977
  _tickPromiseRejecter = new WeakMap();
815
978
  _messageChain = new WeakMap();
979
+ _snapshotTracker = new WeakMap();
980
+ _activeSnapshotRequests = new WeakMap();
981
+ _midStreamPromise = new WeakMap();
982
+ _midStreamPromiseResolver = new WeakMap();
816
983
  _ShapeStream_instances = new WeakSet();
817
984
  start_fn = function() {
818
985
  return __async(this, null, function* () {
@@ -901,15 +1068,13 @@ requestShape_fn = function() {
901
1068
  return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
902
1069
  });
903
1070
  };
904
- constructUrl_fn = function(url, resumingFromPause) {
1071
+ constructUrl_fn = function(url, resumingFromPause, subsetParams) {
905
1072
  return __async(this, null, function* () {
906
1073
  const [requestHeaders, params] = yield Promise.all([
907
1074
  resolveHeaders(this.options.headers),
908
1075
  this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
909
1076
  ]);
910
- if (params) {
911
- validateParams(params);
912
- }
1077
+ if (params) validateParams(params);
913
1078
  const fetchUrl = new URL(url);
914
1079
  if (params) {
915
1080
  if (params.table) setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table);
@@ -929,7 +1094,20 @@ constructUrl_fn = function(url, resumingFromPause) {
929
1094
  setQueryParam(fetchUrl, key, value);
930
1095
  }
931
1096
  }
1097
+ if (subsetParams) {
1098
+ if (subsetParams.where)
1099
+ setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, subsetParams.where);
1100
+ if (subsetParams.params)
1101
+ setQueryParam(fetchUrl, SUBSET_PARAM_WHERE_PARAMS, subsetParams.params);
1102
+ if (subsetParams.limit)
1103
+ setQueryParam(fetchUrl, SUBSET_PARAM_LIMIT, subsetParams.limit);
1104
+ if (subsetParams.offset)
1105
+ setQueryParam(fetchUrl, SUBSET_PARAM_OFFSET, subsetParams.offset);
1106
+ if (subsetParams.orderBy)
1107
+ setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, subsetParams.orderBy);
1108
+ }
932
1109
  fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
1110
+ fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, __privateGet(this, _mode));
933
1111
  if (__privateGet(this, _isUpToDate)) {
934
1112
  if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
935
1113
  fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
@@ -999,7 +1177,9 @@ onInitialResponse_fn = function(response) {
999
1177
  };
1000
1178
  onMessages_fn = function(batch, isSseMessage = false) {
1001
1179
  return __async(this, null, function* () {
1180
+ var _a;
1002
1181
  if (batch.length > 0) {
1182
+ __privateSet(this, _isMidStream, true);
1003
1183
  const lastMessage = batch[batch.length - 1];
1004
1184
  if (isUpToDateMessage(lastMessage)) {
1005
1185
  if (isSseMessage) {
@@ -1010,8 +1190,16 @@ onMessages_fn = function(batch, isSseMessage = false) {
1010
1190
  }
1011
1191
  __privateSet(this, _lastSyncedAt, Date.now());
1012
1192
  __privateSet(this, _isUpToDate, true);
1193
+ __privateSet(this, _isMidStream, false);
1194
+ (_a = __privateGet(this, _midStreamPromiseResolver)) == null ? void 0 : _a.call(this);
1013
1195
  }
1014
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
1196
+ const messagesToProcess = batch.filter((message) => {
1197
+ if (isChangeMessage(message)) {
1198
+ return !__privateGet(this, _snapshotTracker).shouldRejectMessage(message);
1199
+ }
1200
+ return true;
1201
+ });
1202
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messagesToProcess);
1015
1203
  }
1016
1204
  });
1017
1205
  };
@@ -1109,6 +1297,24 @@ nextTick_fn = function() {
1109
1297
  return __privateGet(this, _tickPromise);
1110
1298
  });
1111
1299
  };
1300
+ waitForStreamEnd_fn = function() {
1301
+ return __async(this, null, function* () {
1302
+ if (!__privateGet(this, _isMidStream)) {
1303
+ return;
1304
+ }
1305
+ if (__privateGet(this, _midStreamPromise)) {
1306
+ return __privateGet(this, _midStreamPromise);
1307
+ }
1308
+ __privateSet(this, _midStreamPromise, new Promise((resolve) => {
1309
+ __privateSet(this, _midStreamPromiseResolver, resolve);
1310
+ }));
1311
+ __privateGet(this, _midStreamPromise).finally(() => {
1312
+ __privateSet(this, _midStreamPromise, void 0);
1313
+ __privateSet(this, _midStreamPromiseResolver, void 0);
1314
+ });
1315
+ return __privateGet(this, _midStreamPromise);
1316
+ });
1317
+ };
1112
1318
  publish_fn = function(messages) {
1113
1319
  return __async(this, null, function* () {
1114
1320
  __privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
@@ -1153,8 +1359,33 @@ reset_fn = function(handle) {
1153
1359
  __privateSet(this, _liveCacheBuster, ``);
1154
1360
  __privateSet(this, _shapeHandle, handle);
1155
1361
  __privateSet(this, _isUpToDate, false);
1362
+ __privateSet(this, _isMidStream, true);
1156
1363
  __privateSet(this, _connected, false);
1157
1364
  __privateSet(this, _schema, void 0);
1365
+ __privateSet(this, _activeSnapshotRequests, 0);
1366
+ };
1367
+ fetchSnapshot_fn = function(url, headers) {
1368
+ return __async(this, null, function* () {
1369
+ const response = yield __privateGet(this, _fetchClient2).call(this, url.toString(), { headers });
1370
+ if (!response.ok) {
1371
+ throw new FetchError(
1372
+ response.status,
1373
+ void 0,
1374
+ void 0,
1375
+ Object.fromEntries([...response.headers.entries()]),
1376
+ url.toString()
1377
+ );
1378
+ }
1379
+ const { metadata, data } = yield response.json();
1380
+ const batch = __privateGet(this, _messageParser).parse(
1381
+ JSON.stringify(data),
1382
+ __privateGet(this, _schema)
1383
+ );
1384
+ return {
1385
+ metadata,
1386
+ data: batch
1387
+ };
1388
+ });
1158
1389
  };
1159
1390
  ShapeStream.Replica = {
1160
1391
  FULL: `full`,
@@ -1176,7 +1407,7 @@ function validateOptions(options) {
1176
1407
  if (options.signal && !(options.signal instanceof AbortSignal)) {
1177
1408
  throw new InvalidSignalError();
1178
1409
  }
1179
- if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
1410
+ if (options.offset !== void 0 && options.offset !== `-1` && options.offset !== `now` && !options.handle) {
1180
1411
  throw new MissingShapeHandleError();
1181
1412
  }
1182
1413
  validateParams(options.params);
@@ -1205,12 +1436,15 @@ function convertWhereParamsToObj(allPgParams) {
1205
1436
  }
1206
1437
 
1207
1438
  // src/shape.ts
1208
- var _data, _subscribers2, _status, _error2, _Shape_instances, process_fn, updateShapeStatus_fn, handleError_fn, notify_fn;
1439
+ var _data, _subscribers2, _insertedKeys, _requestedSubSnapshots, _reexecuteSnapshotsPending, _status, _error2, _Shape_instances, process_fn, reexecuteSnapshots_fn, awaitUpToDate_fn, updateShapeStatus_fn, handleError_fn, notify_fn;
1209
1440
  var Shape = class {
1210
1441
  constructor(stream) {
1211
1442
  __privateAdd(this, _Shape_instances);
1212
1443
  __privateAdd(this, _data, /* @__PURE__ */ new Map());
1213
1444
  __privateAdd(this, _subscribers2, /* @__PURE__ */ new Map());
1445
+ __privateAdd(this, _insertedKeys, /* @__PURE__ */ new Set());
1446
+ __privateAdd(this, _requestedSubSnapshots, /* @__PURE__ */ new Set());
1447
+ __privateAdd(this, _reexecuteSnapshotsPending, false);
1214
1448
  __privateAdd(this, _status, `syncing`);
1215
1449
  __privateAdd(this, _error2, false);
1216
1450
  this.stream = stream;
@@ -1269,6 +1503,22 @@ var Shape = class {
1269
1503
  isConnected() {
1270
1504
  return this.stream.isConnected();
1271
1505
  }
1506
+ /** Current log mode of the underlying stream */
1507
+ get mode() {
1508
+ return this.stream.mode;
1509
+ }
1510
+ /**
1511
+ * Request a snapshot for subset of data. Only available when mode is changes_only.
1512
+ * Returns void; data will be emitted via the stream and processed by this Shape.
1513
+ */
1514
+ requestSnapshot(params) {
1515
+ return __async(this, null, function* () {
1516
+ const key = JSON.stringify(params);
1517
+ __privateGet(this, _requestedSubSnapshots).add(key);
1518
+ yield __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
1519
+ yield this.stream.requestSnapshot(params);
1520
+ });
1521
+ }
1272
1522
  subscribe(callback) {
1273
1523
  const subscriptionId = Math.random();
1274
1524
  __privateGet(this, _subscribers2).set(subscriptionId, callback);
@@ -1285,6 +1535,9 @@ var Shape = class {
1285
1535
  };
1286
1536
  _data = new WeakMap();
1287
1537
  _subscribers2 = new WeakMap();
1538
+ _insertedKeys = new WeakMap();
1539
+ _requestedSubSnapshots = new WeakMap();
1540
+ _reexecuteSnapshotsPending = new WeakMap();
1288
1541
  _status = new WeakMap();
1289
1542
  _error2 = new WeakMap();
1290
1543
  _Shape_instances = new WeakSet();
@@ -1293,33 +1546,93 @@ process_fn = function(messages) {
1293
1546
  messages.forEach((message) => {
1294
1547
  if (isChangeMessage(message)) {
1295
1548
  shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
1296
- switch (message.headers.operation) {
1297
- case `insert`:
1298
- __privateGet(this, _data).set(message.key, message.value);
1299
- break;
1300
- case `update`:
1301
- __privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
1302
- break;
1303
- case `delete`:
1304
- __privateGet(this, _data).delete(message.key);
1305
- break;
1549
+ if (this.mode === `full`) {
1550
+ switch (message.headers.operation) {
1551
+ case `insert`:
1552
+ __privateGet(this, _data).set(message.key, message.value);
1553
+ break;
1554
+ case `update`:
1555
+ __privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
1556
+ break;
1557
+ case `delete`:
1558
+ __privateGet(this, _data).delete(message.key);
1559
+ break;
1560
+ }
1561
+ } else {
1562
+ switch (message.headers.operation) {
1563
+ case `insert`:
1564
+ __privateGet(this, _insertedKeys).add(message.key);
1565
+ __privateGet(this, _data).set(message.key, message.value);
1566
+ break;
1567
+ case `update`:
1568
+ if (__privateGet(this, _insertedKeys).has(message.key)) {
1569
+ __privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
1570
+ }
1571
+ break;
1572
+ case `delete`:
1573
+ if (__privateGet(this, _insertedKeys).has(message.key)) {
1574
+ __privateGet(this, _data).delete(message.key);
1575
+ __privateGet(this, _insertedKeys).delete(message.key);
1576
+ }
1577
+ break;
1578
+ }
1306
1579
  }
1307
1580
  }
1308
1581
  if (isControlMessage(message)) {
1309
1582
  switch (message.headers.control) {
1310
1583
  case `up-to-date`:
1311
1584
  shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `up-to-date`);
1585
+ if (__privateGet(this, _reexecuteSnapshotsPending)) {
1586
+ __privateSet(this, _reexecuteSnapshotsPending, false);
1587
+ void __privateMethod(this, _Shape_instances, reexecuteSnapshots_fn).call(this);
1588
+ }
1312
1589
  break;
1313
1590
  case `must-refetch`:
1314
1591
  __privateGet(this, _data).clear();
1592
+ __privateGet(this, _insertedKeys).clear();
1315
1593
  __privateSet(this, _error2, false);
1316
1594
  shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
1595
+ __privateSet(this, _reexecuteSnapshotsPending, true);
1317
1596
  break;
1318
1597
  }
1319
1598
  }
1320
1599
  });
1321
1600
  if (shouldNotify) __privateMethod(this, _Shape_instances, notify_fn).call(this);
1322
1601
  };
1602
+ reexecuteSnapshots_fn = function() {
1603
+ return __async(this, null, function* () {
1604
+ yield __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
1605
+ yield Promise.all(
1606
+ Array.from(__privateGet(this, _requestedSubSnapshots)).map((jsonParams) => __async(this, null, function* () {
1607
+ try {
1608
+ const snapshot = JSON.parse(jsonParams);
1609
+ yield this.stream.requestSnapshot(snapshot);
1610
+ } catch (_) {
1611
+ }
1612
+ }))
1613
+ );
1614
+ });
1615
+ };
1616
+ awaitUpToDate_fn = function() {
1617
+ return __async(this, null, function* () {
1618
+ if (this.stream.isUpToDate) return;
1619
+ yield new Promise((resolve) => {
1620
+ const check = () => {
1621
+ if (this.stream.isUpToDate) {
1622
+ clearInterval(interval);
1623
+ unsub();
1624
+ resolve();
1625
+ }
1626
+ };
1627
+ const interval = setInterval(check, 10);
1628
+ const unsub = this.stream.subscribe(
1629
+ () => check(),
1630
+ () => check()
1631
+ );
1632
+ check();
1633
+ });
1634
+ });
1635
+ };
1323
1636
  updateShapeStatus_fn = function(status) {
1324
1637
  const stateChanged = __privateGet(this, _status) !== status;
1325
1638
  __privateSet(this, _status, status);
@@ -1345,6 +1658,7 @@ notify_fn = function() {
1345
1658
  ShapeStream,
1346
1659
  isChangeMessage,
1347
1660
  isControlMessage,
1661
+ isVisibleInSnapshot,
1348
1662
  resolveValue
1349
1663
  });
1350
1664
  //# sourceMappingURL=index.cjs.map