@electric-sql/client 1.0.9 → 1.0.11
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/README.md +1 -1
- package/dist/cjs/index.cjs +421 -26
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +93 -6
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +93 -6
- package/dist/index.legacy-esm.js +408 -26
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +420 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
- package/src/client.ts +243 -8
- package/src/constants.ts +14 -0
- package/src/expired-shapes-cache.ts +72 -0
- package/src/fetch.ts +19 -1
- package/src/helpers.ts +34 -1
- package/src/index.ts +5 -1
- package/src/parser.ts +12 -1
- package/src/shape.ts +104 -14
- package/src/snapshot-tracker.ts +88 -0
- package/src/types.ts +48 -8
package/dist/index.legacy-esm.js
CHANGED
|
@@ -37,6 +37,14 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
37
37
|
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);
|
|
38
38
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
39
39
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
40
|
+
var __privateWrapper = (obj, member, setter, getter) => ({
|
|
41
|
+
set _(value) {
|
|
42
|
+
__privateSet(obj, member, value, setter);
|
|
43
|
+
},
|
|
44
|
+
get _() {
|
|
45
|
+
return __privateGet(obj, member, getter);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
40
48
|
|
|
41
49
|
// src/error.ts
|
|
42
50
|
var FetchError = class _FetchError extends Error {
|
|
@@ -188,8 +196,9 @@ function pgArrayParser(value, parser) {
|
|
|
188
196
|
return loop(value)[0];
|
|
189
197
|
}
|
|
190
198
|
var MessageParser = class {
|
|
191
|
-
constructor(parser) {
|
|
199
|
+
constructor(parser, transformer) {
|
|
192
200
|
this.parser = __spreadValues(__spreadValues({}, defaultParser), parser);
|
|
201
|
+
this.transformer = transformer;
|
|
193
202
|
}
|
|
194
203
|
parse(messages, schema) {
|
|
195
204
|
return JSON.parse(messages, (key, value) => {
|
|
@@ -198,6 +207,7 @@ var MessageParser = class {
|
|
|
198
207
|
Object.keys(row).forEach((key2) => {
|
|
199
208
|
row[key2] = this.parseRow(key2, row[key2], schema);
|
|
200
209
|
});
|
|
210
|
+
if (this.transformer) value = this.transformer(value);
|
|
201
211
|
}
|
|
202
212
|
return value;
|
|
203
213
|
});
|
|
@@ -254,6 +264,13 @@ function getOffset(message) {
|
|
|
254
264
|
}
|
|
255
265
|
return `${lsn}_0`;
|
|
256
266
|
}
|
|
267
|
+
function isVisibleInSnapshot(txid, snapshot) {
|
|
268
|
+
const xid = BigInt(txid);
|
|
269
|
+
const xmin = BigInt(snapshot.xmin);
|
|
270
|
+
const xmax = BigInt(snapshot.xmax);
|
|
271
|
+
const xip = snapshot.xip_list.map(BigInt);
|
|
272
|
+
return xid < xmin || xid < xmax && !xip.includes(xid);
|
|
273
|
+
}
|
|
257
274
|
|
|
258
275
|
// src/constants.ts
|
|
259
276
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
@@ -263,6 +280,7 @@ var SHAPE_SCHEMA_HEADER = `electric-schema`;
|
|
|
263
280
|
var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
|
|
264
281
|
var COLUMNS_QUERY_PARAM = `columns`;
|
|
265
282
|
var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
|
|
283
|
+
var EXPIRED_HANDLE_QUERY_PARAM = `expired_handle`;
|
|
266
284
|
var SHAPE_HANDLE_QUERY_PARAM = `handle`;
|
|
267
285
|
var LIVE_QUERY_PARAM = `live`;
|
|
268
286
|
var OFFSET_QUERY_PARAM = `offset`;
|
|
@@ -273,11 +291,24 @@ var WHERE_PARAMS_PARAM = `params`;
|
|
|
273
291
|
var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
274
292
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
275
293
|
var PAUSE_STREAM = `pause-stream`;
|
|
294
|
+
var LOG_MODE_QUERY_PARAM = `log`;
|
|
295
|
+
var SUBSET_PARAM_WHERE = `subset__where`;
|
|
296
|
+
var SUBSET_PARAM_LIMIT = `subset__limit`;
|
|
297
|
+
var SUBSET_PARAM_OFFSET = `subset__offset`;
|
|
298
|
+
var SUBSET_PARAM_ORDER_BY = `subset__order_by`;
|
|
299
|
+
var SUBSET_PARAM_WHERE_PARAMS = `subset__params`;
|
|
276
300
|
var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
277
301
|
LIVE_QUERY_PARAM,
|
|
278
302
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
279
303
|
OFFSET_QUERY_PARAM,
|
|
280
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM
|
|
304
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
305
|
+
EXPIRED_HANDLE_QUERY_PARAM,
|
|
306
|
+
LOG_MODE_QUERY_PARAM,
|
|
307
|
+
SUBSET_PARAM_WHERE,
|
|
308
|
+
SUBSET_PARAM_LIMIT,
|
|
309
|
+
SUBSET_PARAM_OFFSET,
|
|
310
|
+
SUBSET_PARAM_ORDER_BY,
|
|
311
|
+
SUBSET_PARAM_WHERE_PARAMS
|
|
281
312
|
];
|
|
282
313
|
|
|
283
314
|
// src/fetch.ts
|
|
@@ -388,10 +419,20 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
|
388
419
|
const headers = response.headers;
|
|
389
420
|
const missingHeaders = [];
|
|
390
421
|
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
391
|
-
addMissingHeaders(requiredElectricResponseHeaders);
|
|
392
422
|
const input = args[0];
|
|
393
423
|
const urlString = input.toString();
|
|
394
424
|
const url = new URL(urlString);
|
|
425
|
+
const isSnapshotRequest = [
|
|
426
|
+
SUBSET_PARAM_WHERE,
|
|
427
|
+
SUBSET_PARAM_WHERE_PARAMS,
|
|
428
|
+
SUBSET_PARAM_LIMIT,
|
|
429
|
+
SUBSET_PARAM_OFFSET,
|
|
430
|
+
SUBSET_PARAM_ORDER_BY
|
|
431
|
+
].some((p) => url.searchParams.has(p));
|
|
432
|
+
if (isSnapshotRequest) {
|
|
433
|
+
return response;
|
|
434
|
+
}
|
|
435
|
+
addMissingHeaders(requiredElectricResponseHeaders);
|
|
395
436
|
if (url.searchParams.get(LIVE_QUERY_PARAM) === `true`) {
|
|
396
437
|
addMissingHeaders(requiredLiveResponseHeaders);
|
|
397
438
|
}
|
|
@@ -507,6 +548,123 @@ function noop() {
|
|
|
507
548
|
import {
|
|
508
549
|
fetchEventSource
|
|
509
550
|
} from "@microsoft/fetch-event-source";
|
|
551
|
+
|
|
552
|
+
// src/expired-shapes-cache.ts
|
|
553
|
+
var ExpiredShapesCache = class {
|
|
554
|
+
constructor() {
|
|
555
|
+
this.data = {};
|
|
556
|
+
this.max = 250;
|
|
557
|
+
this.storageKey = `electric_expired_shapes`;
|
|
558
|
+
this.load();
|
|
559
|
+
}
|
|
560
|
+
getExpiredHandle(shapeUrl) {
|
|
561
|
+
const entry = this.data[shapeUrl];
|
|
562
|
+
if (entry) {
|
|
563
|
+
entry.lastUsed = Date.now();
|
|
564
|
+
this.save();
|
|
565
|
+
return entry.expiredHandle;
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
markExpired(shapeUrl, handle) {
|
|
570
|
+
this.data[shapeUrl] = { expiredHandle: handle, lastUsed: Date.now() };
|
|
571
|
+
const keys = Object.keys(this.data);
|
|
572
|
+
if (keys.length > this.max) {
|
|
573
|
+
const oldest = keys.reduce(
|
|
574
|
+
(min, k) => this.data[k].lastUsed < this.data[min].lastUsed ? k : min
|
|
575
|
+
);
|
|
576
|
+
delete this.data[oldest];
|
|
577
|
+
}
|
|
578
|
+
this.save();
|
|
579
|
+
}
|
|
580
|
+
save() {
|
|
581
|
+
if (typeof localStorage === `undefined`) return;
|
|
582
|
+
try {
|
|
583
|
+
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
|
|
584
|
+
} catch (e) {
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
load() {
|
|
588
|
+
if (typeof localStorage === `undefined`) return;
|
|
589
|
+
try {
|
|
590
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
591
|
+
if (stored) {
|
|
592
|
+
this.data = JSON.parse(stored);
|
|
593
|
+
}
|
|
594
|
+
} catch (e) {
|
|
595
|
+
this.data = {};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
clear() {
|
|
599
|
+
this.data = {};
|
|
600
|
+
this.save();
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
var expiredShapesCache = new ExpiredShapesCache();
|
|
604
|
+
|
|
605
|
+
// src/snapshot-tracker.ts
|
|
606
|
+
var SnapshotTracker = class {
|
|
607
|
+
constructor() {
|
|
608
|
+
this.activeSnapshots = /* @__PURE__ */ new Map();
|
|
609
|
+
this.xmaxSnapshots = /* @__PURE__ */ new Map();
|
|
610
|
+
this.snapshotsByDatabaseLsn = /* @__PURE__ */ new Map();
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Add a new snapshot for tracking
|
|
614
|
+
*/
|
|
615
|
+
addSnapshot(metadata, keys) {
|
|
616
|
+
var _a, _b, _c, _d;
|
|
617
|
+
this.activeSnapshots.set(metadata.snapshot_mark, {
|
|
618
|
+
xmin: BigInt(metadata.xmin),
|
|
619
|
+
xmax: BigInt(metadata.xmax),
|
|
620
|
+
xip_list: metadata.xip_list.map(BigInt),
|
|
621
|
+
keys
|
|
622
|
+
});
|
|
623
|
+
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]);
|
|
624
|
+
this.xmaxSnapshots.set(BigInt(metadata.xmax), xmaxSet);
|
|
625
|
+
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]);
|
|
626
|
+
this.snapshotsByDatabaseLsn.set(
|
|
627
|
+
BigInt(metadata.database_lsn),
|
|
628
|
+
databaseLsnSet
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Remove a snapshot from tracking
|
|
633
|
+
*/
|
|
634
|
+
removeSnapshot(snapshotMark) {
|
|
635
|
+
this.activeSnapshots.delete(snapshotMark);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Check if a change message should be filtered because its already in an active snapshot
|
|
639
|
+
* Returns true if the message should be filtered out (not processed)
|
|
640
|
+
*/
|
|
641
|
+
shouldRejectMessage(message) {
|
|
642
|
+
const txids = message.headers.txids || [];
|
|
643
|
+
if (txids.length === 0) return false;
|
|
644
|
+
const xid = Math.max(...txids);
|
|
645
|
+
for (const [xmax, snapshots] of this.xmaxSnapshots.entries()) {
|
|
646
|
+
if (xid >= xmax) {
|
|
647
|
+
for (const snapshot of snapshots) {
|
|
648
|
+
this.removeSnapshot(snapshot);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return [...this.activeSnapshots.values()].some(
|
|
653
|
+
(x) => x.keys.has(message.key) && isVisibleInSnapshot(xid, x)
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
lastSeenUpdate(newDatabaseLsn) {
|
|
657
|
+
for (const [dbLsn, snapshots] of this.snapshotsByDatabaseLsn.entries()) {
|
|
658
|
+
if (dbLsn <= newDatabaseLsn) {
|
|
659
|
+
for (const snapshot of snapshots) {
|
|
660
|
+
this.removeSnapshot(snapshot);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/client.ts
|
|
510
668
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
511
669
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
512
670
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
@@ -543,9 +701,18 @@ async function resolveHeaders(headers) {
|
|
|
543
701
|
);
|
|
544
702
|
return Object.fromEntries(resolvedEntries);
|
|
545
703
|
}
|
|
546
|
-
|
|
704
|
+
function canonicalShapeKey(url) {
|
|
705
|
+
const cleanUrl = new URL(url.origin + url.pathname);
|
|
706
|
+
for (const [key, value] of url.searchParams) {
|
|
707
|
+
if (!ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) {
|
|
708
|
+
cleanUrl.searchParams.set(key, value);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
cleanUrl.searchParams.sort();
|
|
712
|
+
return cleanUrl.toString();
|
|
713
|
+
}
|
|
714
|
+
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;
|
|
547
715
|
var ShapeStream = class {
|
|
548
|
-
// promise chain for incoming messages
|
|
549
716
|
constructor(options) {
|
|
550
717
|
__privateAdd(this, _ShapeStream_instances);
|
|
551
718
|
__privateAdd(this, _error, null);
|
|
@@ -561,8 +728,10 @@ var ShapeStream = class {
|
|
|
561
728
|
__privateAdd(this, _lastSyncedAt);
|
|
562
729
|
// unix time
|
|
563
730
|
__privateAdd(this, _isUpToDate, false);
|
|
731
|
+
__privateAdd(this, _isMidStream, true);
|
|
564
732
|
__privateAdd(this, _connected, false);
|
|
565
733
|
__privateAdd(this, _shapeHandle);
|
|
734
|
+
__privateAdd(this, _mode);
|
|
566
735
|
__privateAdd(this, _schema);
|
|
567
736
|
__privateAdd(this, _onError);
|
|
568
737
|
__privateAdd(this, _requestAbortController);
|
|
@@ -571,16 +740,26 @@ var ShapeStream = class {
|
|
|
571
740
|
__privateAdd(this, _tickPromiseResolver);
|
|
572
741
|
__privateAdd(this, _tickPromiseRejecter);
|
|
573
742
|
__privateAdd(this, _messageChain, Promise.resolve([]));
|
|
574
|
-
|
|
743
|
+
// promise chain for incoming messages
|
|
744
|
+
__privateAdd(this, _snapshotTracker, new SnapshotTracker());
|
|
745
|
+
__privateAdd(this, _activeSnapshotRequests, 0);
|
|
746
|
+
// counter for concurrent snapshot requests
|
|
747
|
+
__privateAdd(this, _midStreamPromise);
|
|
748
|
+
__privateAdd(this, _midStreamPromiseResolver);
|
|
749
|
+
var _a, _b, _c, _d;
|
|
575
750
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
576
751
|
validateOptions(this.options);
|
|
577
752
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
578
753
|
__privateSet(this, _liveCacheBuster, ``);
|
|
579
754
|
__privateSet(this, _shapeHandle, this.options.handle);
|
|
580
|
-
__privateSet(this, _messageParser, new MessageParser(
|
|
755
|
+
__privateSet(this, _messageParser, new MessageParser(
|
|
756
|
+
options.parser,
|
|
757
|
+
options.transformer
|
|
758
|
+
));
|
|
581
759
|
__privateSet(this, _onError, this.options.onError);
|
|
582
|
-
|
|
583
|
-
const
|
|
760
|
+
__privateSet(this, _mode, (_b = this.options.mode) != null ? _b : `full`);
|
|
761
|
+
const baseFetchClient = (_c = options.fetchClient) != null ? _c : (...args) => fetch(...args);
|
|
762
|
+
const backOffOpts = __spreadProps(__spreadValues({}, (_d = options.backoffOptions) != null ? _d : BackoffDefaults), {
|
|
584
763
|
onFailedAttempt: () => {
|
|
585
764
|
var _a2, _b2;
|
|
586
765
|
__privateSet(this, _connected, false);
|
|
@@ -609,6 +788,9 @@ var ShapeStream = class {
|
|
|
609
788
|
get lastOffset() {
|
|
610
789
|
return __privateGet(this, _lastOffset);
|
|
611
790
|
}
|
|
791
|
+
get mode() {
|
|
792
|
+
return __privateGet(this, _mode);
|
|
793
|
+
}
|
|
612
794
|
subscribe(callback, onError = () => {
|
|
613
795
|
}) {
|
|
614
796
|
const subscriptionId = Math.random();
|
|
@@ -659,6 +841,54 @@ var ShapeStream = class {
|
|
|
659
841
|
await __privateMethod(this, _ShapeStream_instances, nextTick_fn).call(this);
|
|
660
842
|
__privateSet(this, _isRefreshing, false);
|
|
661
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* Request a snapshot for subset of data.
|
|
846
|
+
*
|
|
847
|
+
* Only available when mode is `changes_only`.
|
|
848
|
+
* Returns the insertion point & the data, but more importantly injects the data
|
|
849
|
+
* into the subscribed data stream. Returned value is unlikely to be useful for the caller,
|
|
850
|
+
* unless the caller has complicated additional logic.
|
|
851
|
+
*
|
|
852
|
+
* Data will be injected in a way that's also tracking further incoming changes, and it'll
|
|
853
|
+
* skip the ones that are already in the snapshot.
|
|
854
|
+
*
|
|
855
|
+
* @param opts - The options for the snapshot request.
|
|
856
|
+
* @returns The metadata and the data for the snapshot.
|
|
857
|
+
*/
|
|
858
|
+
async requestSnapshot(opts) {
|
|
859
|
+
if (__privateGet(this, _mode) === `full`) {
|
|
860
|
+
throw new Error(
|
|
861
|
+
`Snapshot requests are not supported in ${__privateGet(this, _mode)} mode, as the consumer is guaranteed to observe all data`
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
if (!__privateGet(this, _started)) await __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
865
|
+
await __privateMethod(this, _ShapeStream_instances, waitForStreamEnd_fn).call(this);
|
|
866
|
+
__privateWrapper(this, _activeSnapshotRequests)._++;
|
|
867
|
+
try {
|
|
868
|
+
if (__privateGet(this, _activeSnapshotRequests) === 1) {
|
|
869
|
+
__privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
|
|
870
|
+
}
|
|
871
|
+
const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
|
|
872
|
+
const { metadata, data } = await __privateMethod(this, _ShapeStream_instances, fetchSnapshot_fn).call(this, fetchUrl, requestHeaders);
|
|
873
|
+
const dataWithEndBoundary = data.concat([
|
|
874
|
+
{ headers: __spreadValues({ control: `snapshot-end` }, metadata) }
|
|
875
|
+
]);
|
|
876
|
+
__privateGet(this, _snapshotTracker).addSnapshot(
|
|
877
|
+
metadata,
|
|
878
|
+
new Set(data.map((message) => message.key))
|
|
879
|
+
);
|
|
880
|
+
__privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, dataWithEndBoundary, false);
|
|
881
|
+
return {
|
|
882
|
+
metadata,
|
|
883
|
+
data
|
|
884
|
+
};
|
|
885
|
+
} finally {
|
|
886
|
+
__privateWrapper(this, _activeSnapshotRequests)._--;
|
|
887
|
+
if (__privateGet(this, _activeSnapshotRequests) === 0) {
|
|
888
|
+
__privateMethod(this, _ShapeStream_instances, resume_fn).call(this);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
662
892
|
};
|
|
663
893
|
_error = new WeakMap();
|
|
664
894
|
_fetchClient2 = new WeakMap();
|
|
@@ -671,8 +901,10 @@ _lastOffset = new WeakMap();
|
|
|
671
901
|
_liveCacheBuster = new WeakMap();
|
|
672
902
|
_lastSyncedAt = new WeakMap();
|
|
673
903
|
_isUpToDate = new WeakMap();
|
|
904
|
+
_isMidStream = new WeakMap();
|
|
674
905
|
_connected = new WeakMap();
|
|
675
906
|
_shapeHandle = new WeakMap();
|
|
907
|
+
_mode = new WeakMap();
|
|
676
908
|
_schema = new WeakMap();
|
|
677
909
|
_onError = new WeakMap();
|
|
678
910
|
_requestAbortController = new WeakMap();
|
|
@@ -681,6 +913,10 @@ _tickPromise = new WeakMap();
|
|
|
681
913
|
_tickPromiseResolver = new WeakMap();
|
|
682
914
|
_tickPromiseRejecter = new WeakMap();
|
|
683
915
|
_messageChain = new WeakMap();
|
|
916
|
+
_snapshotTracker = new WeakMap();
|
|
917
|
+
_activeSnapshotRequests = new WeakMap();
|
|
918
|
+
_midStreamPromise = new WeakMap();
|
|
919
|
+
_midStreamPromiseResolver = new WeakMap();
|
|
684
920
|
_ShapeStream_instances = new WeakSet();
|
|
685
921
|
start_fn = async function() {
|
|
686
922
|
var _a;
|
|
@@ -744,6 +980,10 @@ requestShape_fn = async function() {
|
|
|
744
980
|
}
|
|
745
981
|
if (!(e instanceof FetchError)) throw e;
|
|
746
982
|
if (e.status == 409) {
|
|
983
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
984
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
985
|
+
expiredShapesCache.markExpired(shapeKey, __privateGet(this, _shapeHandle));
|
|
986
|
+
}
|
|
747
987
|
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this, _shapeHandle)}-next`;
|
|
748
988
|
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
749
989
|
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
@@ -761,14 +1001,12 @@ requestShape_fn = async function() {
|
|
|
761
1001
|
(_b = __privateGet(this, _tickPromiseResolver)) == null ? void 0 : _b.call(this);
|
|
762
1002
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
763
1003
|
};
|
|
764
|
-
constructUrl_fn = async function(url, resumingFromPause) {
|
|
1004
|
+
constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
|
|
765
1005
|
const [requestHeaders, params] = await Promise.all([
|
|
766
1006
|
resolveHeaders(this.options.headers),
|
|
767
1007
|
this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
|
|
768
1008
|
]);
|
|
769
|
-
if (params)
|
|
770
|
-
validateParams(params);
|
|
771
|
-
}
|
|
1009
|
+
if (params) validateParams(params);
|
|
772
1010
|
const fetchUrl = new URL(url);
|
|
773
1011
|
if (params) {
|
|
774
1012
|
if (params.table) setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table);
|
|
@@ -788,7 +1026,20 @@ constructUrl_fn = async function(url, resumingFromPause) {
|
|
|
788
1026
|
setQueryParam(fetchUrl, key, value);
|
|
789
1027
|
}
|
|
790
1028
|
}
|
|
1029
|
+
if (subsetParams) {
|
|
1030
|
+
if (subsetParams.where)
|
|
1031
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, subsetParams.where);
|
|
1032
|
+
if (subsetParams.params)
|
|
1033
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_WHERE_PARAMS, subsetParams.params);
|
|
1034
|
+
if (subsetParams.limit)
|
|
1035
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_LIMIT, subsetParams.limit);
|
|
1036
|
+
if (subsetParams.offset)
|
|
1037
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_OFFSET, subsetParams.offset);
|
|
1038
|
+
if (subsetParams.orderBy)
|
|
1039
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, subsetParams.orderBy);
|
|
1040
|
+
}
|
|
791
1041
|
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
1042
|
+
fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, __privateGet(this, _mode));
|
|
792
1043
|
if (__privateGet(this, _isUpToDate)) {
|
|
793
1044
|
if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
|
|
794
1045
|
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
@@ -801,6 +1052,11 @@ constructUrl_fn = async function(url, resumingFromPause) {
|
|
|
801
1052
|
if (__privateGet(this, _shapeHandle)) {
|
|
802
1053
|
fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
|
|
803
1054
|
}
|
|
1055
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1056
|
+
const expiredHandle = expiredShapesCache.getExpiredHandle(shapeKey);
|
|
1057
|
+
if (expiredHandle) {
|
|
1058
|
+
fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle);
|
|
1059
|
+
}
|
|
804
1060
|
fetchUrl.searchParams.sort();
|
|
805
1061
|
return {
|
|
806
1062
|
fetchUrl,
|
|
@@ -847,7 +1103,9 @@ onInitialResponse_fn = async function(response) {
|
|
|
847
1103
|
}
|
|
848
1104
|
};
|
|
849
1105
|
onMessages_fn = async function(batch, isSseMessage = false) {
|
|
1106
|
+
var _a;
|
|
850
1107
|
if (batch.length > 0) {
|
|
1108
|
+
__privateSet(this, _isMidStream, true);
|
|
851
1109
|
const lastMessage = batch[batch.length - 1];
|
|
852
1110
|
if (isUpToDateMessage(lastMessage)) {
|
|
853
1111
|
if (isSseMessage) {
|
|
@@ -858,8 +1116,16 @@ onMessages_fn = async function(batch, isSseMessage = false) {
|
|
|
858
1116
|
}
|
|
859
1117
|
__privateSet(this, _lastSyncedAt, Date.now());
|
|
860
1118
|
__privateSet(this, _isUpToDate, true);
|
|
1119
|
+
__privateSet(this, _isMidStream, false);
|
|
1120
|
+
(_a = __privateGet(this, _midStreamPromiseResolver)) == null ? void 0 : _a.call(this);
|
|
861
1121
|
}
|
|
862
|
-
|
|
1122
|
+
const messagesToProcess = batch.filter((message) => {
|
|
1123
|
+
if (isChangeMessage(message)) {
|
|
1124
|
+
return !__privateGet(this, _snapshotTracker).shouldRejectMessage(message);
|
|
1125
|
+
}
|
|
1126
|
+
return true;
|
|
1127
|
+
});
|
|
1128
|
+
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messagesToProcess);
|
|
863
1129
|
}
|
|
864
1130
|
};
|
|
865
1131
|
fetchShape_fn = async function(opts) {
|
|
@@ -948,6 +1214,22 @@ nextTick_fn = async function() {
|
|
|
948
1214
|
});
|
|
949
1215
|
return __privateGet(this, _tickPromise);
|
|
950
1216
|
};
|
|
1217
|
+
waitForStreamEnd_fn = async function() {
|
|
1218
|
+
if (!__privateGet(this, _isMidStream)) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (__privateGet(this, _midStreamPromise)) {
|
|
1222
|
+
return __privateGet(this, _midStreamPromise);
|
|
1223
|
+
}
|
|
1224
|
+
__privateSet(this, _midStreamPromise, new Promise((resolve) => {
|
|
1225
|
+
__privateSet(this, _midStreamPromiseResolver, resolve);
|
|
1226
|
+
}));
|
|
1227
|
+
__privateGet(this, _midStreamPromise).finally(() => {
|
|
1228
|
+
__privateSet(this, _midStreamPromise, void 0);
|
|
1229
|
+
__privateSet(this, _midStreamPromiseResolver, void 0);
|
|
1230
|
+
});
|
|
1231
|
+
return __privateGet(this, _midStreamPromise);
|
|
1232
|
+
};
|
|
951
1233
|
publish_fn = async function(messages) {
|
|
952
1234
|
__privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
|
|
953
1235
|
() => Promise.all(
|
|
@@ -990,8 +1272,31 @@ reset_fn = function(handle) {
|
|
|
990
1272
|
__privateSet(this, _liveCacheBuster, ``);
|
|
991
1273
|
__privateSet(this, _shapeHandle, handle);
|
|
992
1274
|
__privateSet(this, _isUpToDate, false);
|
|
1275
|
+
__privateSet(this, _isMidStream, true);
|
|
993
1276
|
__privateSet(this, _connected, false);
|
|
994
1277
|
__privateSet(this, _schema, void 0);
|
|
1278
|
+
__privateSet(this, _activeSnapshotRequests, 0);
|
|
1279
|
+
};
|
|
1280
|
+
fetchSnapshot_fn = async function(url, headers) {
|
|
1281
|
+
const response = await __privateGet(this, _fetchClient2).call(this, url.toString(), { headers });
|
|
1282
|
+
if (!response.ok) {
|
|
1283
|
+
throw new FetchError(
|
|
1284
|
+
response.status,
|
|
1285
|
+
void 0,
|
|
1286
|
+
void 0,
|
|
1287
|
+
Object.fromEntries([...response.headers.entries()]),
|
|
1288
|
+
url.toString()
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
const { metadata, data } = await response.json();
|
|
1292
|
+
const batch = __privateGet(this, _messageParser).parse(
|
|
1293
|
+
JSON.stringify(data),
|
|
1294
|
+
__privateGet(this, _schema)
|
|
1295
|
+
);
|
|
1296
|
+
return {
|
|
1297
|
+
metadata,
|
|
1298
|
+
data: batch
|
|
1299
|
+
};
|
|
995
1300
|
};
|
|
996
1301
|
ShapeStream.Replica = {
|
|
997
1302
|
FULL: `full`,
|
|
@@ -1013,7 +1318,7 @@ function validateOptions(options) {
|
|
|
1013
1318
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
1014
1319
|
throw new InvalidSignalError();
|
|
1015
1320
|
}
|
|
1016
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
1321
|
+
if (options.offset !== void 0 && options.offset !== `-1` && options.offset !== `now` && !options.handle) {
|
|
1017
1322
|
throw new MissingShapeHandleError();
|
|
1018
1323
|
}
|
|
1019
1324
|
validateParams(options.params);
|
|
@@ -1042,12 +1347,15 @@ function convertWhereParamsToObj(allPgParams) {
|
|
|
1042
1347
|
}
|
|
1043
1348
|
|
|
1044
1349
|
// src/shape.ts
|
|
1045
|
-
var _data, _subscribers2, _status, _error2, _Shape_instances, process_fn, updateShapeStatus_fn, handleError_fn, notify_fn;
|
|
1350
|
+
var _data, _subscribers2, _insertedKeys, _requestedSubSnapshots, _reexecuteSnapshotsPending, _status, _error2, _Shape_instances, process_fn, reexecuteSnapshots_fn, awaitUpToDate_fn, updateShapeStatus_fn, handleError_fn, notify_fn;
|
|
1046
1351
|
var Shape = class {
|
|
1047
1352
|
constructor(stream) {
|
|
1048
1353
|
__privateAdd(this, _Shape_instances);
|
|
1049
1354
|
__privateAdd(this, _data, /* @__PURE__ */ new Map());
|
|
1050
1355
|
__privateAdd(this, _subscribers2, /* @__PURE__ */ new Map());
|
|
1356
|
+
__privateAdd(this, _insertedKeys, /* @__PURE__ */ new Set());
|
|
1357
|
+
__privateAdd(this, _requestedSubSnapshots, /* @__PURE__ */ new Set());
|
|
1358
|
+
__privateAdd(this, _reexecuteSnapshotsPending, false);
|
|
1051
1359
|
__privateAdd(this, _status, `syncing`);
|
|
1052
1360
|
__privateAdd(this, _error2, false);
|
|
1053
1361
|
this.stream = stream;
|
|
@@ -1106,6 +1414,20 @@ var Shape = class {
|
|
|
1106
1414
|
isConnected() {
|
|
1107
1415
|
return this.stream.isConnected();
|
|
1108
1416
|
}
|
|
1417
|
+
/** Current log mode of the underlying stream */
|
|
1418
|
+
get mode() {
|
|
1419
|
+
return this.stream.mode;
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Request a snapshot for subset of data. Only available when mode is changes_only.
|
|
1423
|
+
* Returns void; data will be emitted via the stream and processed by this Shape.
|
|
1424
|
+
*/
|
|
1425
|
+
async requestSnapshot(params) {
|
|
1426
|
+
const key = JSON.stringify(params);
|
|
1427
|
+
__privateGet(this, _requestedSubSnapshots).add(key);
|
|
1428
|
+
await __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
|
|
1429
|
+
await this.stream.requestSnapshot(params);
|
|
1430
|
+
}
|
|
1109
1431
|
subscribe(callback) {
|
|
1110
1432
|
const subscriptionId = Math.random();
|
|
1111
1433
|
__privateGet(this, _subscribers2).set(subscriptionId, callback);
|
|
@@ -1122,6 +1444,9 @@ var Shape = class {
|
|
|
1122
1444
|
};
|
|
1123
1445
|
_data = new WeakMap();
|
|
1124
1446
|
_subscribers2 = new WeakMap();
|
|
1447
|
+
_insertedKeys = new WeakMap();
|
|
1448
|
+
_requestedSubSnapshots = new WeakMap();
|
|
1449
|
+
_reexecuteSnapshotsPending = new WeakMap();
|
|
1125
1450
|
_status = new WeakMap();
|
|
1126
1451
|
_error2 = new WeakMap();
|
|
1127
1452
|
_Shape_instances = new WeakSet();
|
|
@@ -1130,33 +1455,89 @@ process_fn = function(messages) {
|
|
|
1130
1455
|
messages.forEach((message) => {
|
|
1131
1456
|
if (isChangeMessage(message)) {
|
|
1132
1457
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1458
|
+
if (this.mode === `full`) {
|
|
1459
|
+
switch (message.headers.operation) {
|
|
1460
|
+
case `insert`:
|
|
1461
|
+
__privateGet(this, _data).set(message.key, message.value);
|
|
1462
|
+
break;
|
|
1463
|
+
case `update`:
|
|
1464
|
+
__privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
|
|
1465
|
+
break;
|
|
1466
|
+
case `delete`:
|
|
1467
|
+
__privateGet(this, _data).delete(message.key);
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1470
|
+
} else {
|
|
1471
|
+
switch (message.headers.operation) {
|
|
1472
|
+
case `insert`:
|
|
1473
|
+
__privateGet(this, _insertedKeys).add(message.key);
|
|
1474
|
+
__privateGet(this, _data).set(message.key, message.value);
|
|
1475
|
+
break;
|
|
1476
|
+
case `update`:
|
|
1477
|
+
if (__privateGet(this, _insertedKeys).has(message.key)) {
|
|
1478
|
+
__privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
|
|
1479
|
+
}
|
|
1480
|
+
break;
|
|
1481
|
+
case `delete`:
|
|
1482
|
+
if (__privateGet(this, _insertedKeys).has(message.key)) {
|
|
1483
|
+
__privateGet(this, _data).delete(message.key);
|
|
1484
|
+
__privateGet(this, _insertedKeys).delete(message.key);
|
|
1485
|
+
}
|
|
1486
|
+
break;
|
|
1487
|
+
}
|
|
1143
1488
|
}
|
|
1144
1489
|
}
|
|
1145
1490
|
if (isControlMessage(message)) {
|
|
1146
1491
|
switch (message.headers.control) {
|
|
1147
1492
|
case `up-to-date`:
|
|
1148
1493
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `up-to-date`);
|
|
1494
|
+
if (__privateGet(this, _reexecuteSnapshotsPending)) {
|
|
1495
|
+
__privateSet(this, _reexecuteSnapshotsPending, false);
|
|
1496
|
+
void __privateMethod(this, _Shape_instances, reexecuteSnapshots_fn).call(this);
|
|
1497
|
+
}
|
|
1149
1498
|
break;
|
|
1150
1499
|
case `must-refetch`:
|
|
1151
1500
|
__privateGet(this, _data).clear();
|
|
1501
|
+
__privateGet(this, _insertedKeys).clear();
|
|
1152
1502
|
__privateSet(this, _error2, false);
|
|
1153
1503
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
|
|
1504
|
+
__privateSet(this, _reexecuteSnapshotsPending, true);
|
|
1154
1505
|
break;
|
|
1155
1506
|
}
|
|
1156
1507
|
}
|
|
1157
1508
|
});
|
|
1158
1509
|
if (shouldNotify) __privateMethod(this, _Shape_instances, notify_fn).call(this);
|
|
1159
1510
|
};
|
|
1511
|
+
reexecuteSnapshots_fn = async function() {
|
|
1512
|
+
await __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
|
|
1513
|
+
await Promise.all(
|
|
1514
|
+
Array.from(__privateGet(this, _requestedSubSnapshots)).map(async (jsonParams) => {
|
|
1515
|
+
try {
|
|
1516
|
+
const snapshot = JSON.parse(jsonParams);
|
|
1517
|
+
await this.stream.requestSnapshot(snapshot);
|
|
1518
|
+
} catch (_) {
|
|
1519
|
+
}
|
|
1520
|
+
})
|
|
1521
|
+
);
|
|
1522
|
+
};
|
|
1523
|
+
awaitUpToDate_fn = async function() {
|
|
1524
|
+
if (this.stream.isUpToDate) return;
|
|
1525
|
+
await new Promise((resolve) => {
|
|
1526
|
+
const check = () => {
|
|
1527
|
+
if (this.stream.isUpToDate) {
|
|
1528
|
+
clearInterval(interval);
|
|
1529
|
+
unsub();
|
|
1530
|
+
resolve();
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
const interval = setInterval(check, 10);
|
|
1534
|
+
const unsub = this.stream.subscribe(
|
|
1535
|
+
() => check(),
|
|
1536
|
+
() => check()
|
|
1537
|
+
);
|
|
1538
|
+
check();
|
|
1539
|
+
});
|
|
1540
|
+
};
|
|
1160
1541
|
updateShapeStatus_fn = function(status) {
|
|
1161
1542
|
const stateChanged = __privateGet(this, _status) !== status;
|
|
1162
1543
|
__privateSet(this, _status, status);
|
|
@@ -1181,6 +1562,7 @@ export {
|
|
|
1181
1562
|
ShapeStream,
|
|
1182
1563
|
isChangeMessage,
|
|
1183
1564
|
isControlMessage,
|
|
1565
|
+
isVisibleInSnapshot,
|
|
1184
1566
|
resolveValue
|
|
1185
1567
|
};
|
|
1186
1568
|
//# sourceMappingURL=index.legacy-esm.js.map
|