@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.mjs
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
|
var __async = (__this, __arguments, generator) => {
|
|
41
49
|
return new Promise((resolve, reject) => {
|
|
42
50
|
var fulfilled = (value) => {
|
|
@@ -210,8 +218,9 @@ function pgArrayParser(value, parser) {
|
|
|
210
218
|
return loop(value)[0];
|
|
211
219
|
}
|
|
212
220
|
var MessageParser = class {
|
|
213
|
-
constructor(parser) {
|
|
221
|
+
constructor(parser, transformer) {
|
|
214
222
|
this.parser = __spreadValues(__spreadValues({}, defaultParser), parser);
|
|
223
|
+
this.transformer = transformer;
|
|
215
224
|
}
|
|
216
225
|
parse(messages, schema) {
|
|
217
226
|
return JSON.parse(messages, (key, value) => {
|
|
@@ -220,6 +229,7 @@ var MessageParser = class {
|
|
|
220
229
|
Object.keys(row).forEach((key2) => {
|
|
221
230
|
row[key2] = this.parseRow(key2, row[key2], schema);
|
|
222
231
|
});
|
|
232
|
+
if (this.transformer) value = this.transformer(value);
|
|
223
233
|
}
|
|
224
234
|
return value;
|
|
225
235
|
});
|
|
@@ -276,6 +286,13 @@ function getOffset(message) {
|
|
|
276
286
|
}
|
|
277
287
|
return `${lsn}_0`;
|
|
278
288
|
}
|
|
289
|
+
function isVisibleInSnapshot(txid, snapshot) {
|
|
290
|
+
const xid = BigInt(txid);
|
|
291
|
+
const xmin = BigInt(snapshot.xmin);
|
|
292
|
+
const xmax = BigInt(snapshot.xmax);
|
|
293
|
+
const xip = snapshot.xip_list.map(BigInt);
|
|
294
|
+
return xid < xmin || xid < xmax && !xip.includes(xid);
|
|
295
|
+
}
|
|
279
296
|
|
|
280
297
|
// src/constants.ts
|
|
281
298
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
@@ -285,6 +302,7 @@ var SHAPE_SCHEMA_HEADER = `electric-schema`;
|
|
|
285
302
|
var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
|
|
286
303
|
var COLUMNS_QUERY_PARAM = `columns`;
|
|
287
304
|
var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
|
|
305
|
+
var EXPIRED_HANDLE_QUERY_PARAM = `expired_handle`;
|
|
288
306
|
var SHAPE_HANDLE_QUERY_PARAM = `handle`;
|
|
289
307
|
var LIVE_QUERY_PARAM = `live`;
|
|
290
308
|
var OFFSET_QUERY_PARAM = `offset`;
|
|
@@ -295,11 +313,24 @@ var WHERE_PARAMS_PARAM = `params`;
|
|
|
295
313
|
var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
296
314
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
297
315
|
var PAUSE_STREAM = `pause-stream`;
|
|
316
|
+
var LOG_MODE_QUERY_PARAM = `log`;
|
|
317
|
+
var SUBSET_PARAM_WHERE = `subset__where`;
|
|
318
|
+
var SUBSET_PARAM_LIMIT = `subset__limit`;
|
|
319
|
+
var SUBSET_PARAM_OFFSET = `subset__offset`;
|
|
320
|
+
var SUBSET_PARAM_ORDER_BY = `subset__order_by`;
|
|
321
|
+
var SUBSET_PARAM_WHERE_PARAMS = `subset__params`;
|
|
298
322
|
var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
299
323
|
LIVE_QUERY_PARAM,
|
|
300
324
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
301
325
|
OFFSET_QUERY_PARAM,
|
|
302
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM
|
|
326
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
327
|
+
EXPIRED_HANDLE_QUERY_PARAM,
|
|
328
|
+
LOG_MODE_QUERY_PARAM,
|
|
329
|
+
SUBSET_PARAM_WHERE,
|
|
330
|
+
SUBSET_PARAM_LIMIT,
|
|
331
|
+
SUBSET_PARAM_OFFSET,
|
|
332
|
+
SUBSET_PARAM_ORDER_BY,
|
|
333
|
+
SUBSET_PARAM_WHERE_PARAMS
|
|
303
334
|
];
|
|
304
335
|
|
|
305
336
|
// src/fetch.ts
|
|
@@ -410,10 +441,20 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
|
410
441
|
const headers = response.headers;
|
|
411
442
|
const missingHeaders = [];
|
|
412
443
|
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
413
|
-
addMissingHeaders(requiredElectricResponseHeaders);
|
|
414
444
|
const input = args[0];
|
|
415
445
|
const urlString = input.toString();
|
|
416
446
|
const url = new URL(urlString);
|
|
447
|
+
const isSnapshotRequest = [
|
|
448
|
+
SUBSET_PARAM_WHERE,
|
|
449
|
+
SUBSET_PARAM_WHERE_PARAMS,
|
|
450
|
+
SUBSET_PARAM_LIMIT,
|
|
451
|
+
SUBSET_PARAM_OFFSET,
|
|
452
|
+
SUBSET_PARAM_ORDER_BY
|
|
453
|
+
].some((p) => url.searchParams.has(p));
|
|
454
|
+
if (isSnapshotRequest) {
|
|
455
|
+
return response;
|
|
456
|
+
}
|
|
457
|
+
addMissingHeaders(requiredElectricResponseHeaders);
|
|
417
458
|
if (url.searchParams.get(LIVE_QUERY_PARAM) === `true`) {
|
|
418
459
|
addMissingHeaders(requiredLiveResponseHeaders);
|
|
419
460
|
}
|
|
@@ -529,6 +570,123 @@ function noop() {
|
|
|
529
570
|
import {
|
|
530
571
|
fetchEventSource
|
|
531
572
|
} from "@microsoft/fetch-event-source";
|
|
573
|
+
|
|
574
|
+
// src/expired-shapes-cache.ts
|
|
575
|
+
var ExpiredShapesCache = class {
|
|
576
|
+
constructor() {
|
|
577
|
+
this.data = {};
|
|
578
|
+
this.max = 250;
|
|
579
|
+
this.storageKey = `electric_expired_shapes`;
|
|
580
|
+
this.load();
|
|
581
|
+
}
|
|
582
|
+
getExpiredHandle(shapeUrl) {
|
|
583
|
+
const entry = this.data[shapeUrl];
|
|
584
|
+
if (entry) {
|
|
585
|
+
entry.lastUsed = Date.now();
|
|
586
|
+
this.save();
|
|
587
|
+
return entry.expiredHandle;
|
|
588
|
+
}
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
markExpired(shapeUrl, handle) {
|
|
592
|
+
this.data[shapeUrl] = { expiredHandle: handle, lastUsed: Date.now() };
|
|
593
|
+
const keys = Object.keys(this.data);
|
|
594
|
+
if (keys.length > this.max) {
|
|
595
|
+
const oldest = keys.reduce(
|
|
596
|
+
(min, k) => this.data[k].lastUsed < this.data[min].lastUsed ? k : min
|
|
597
|
+
);
|
|
598
|
+
delete this.data[oldest];
|
|
599
|
+
}
|
|
600
|
+
this.save();
|
|
601
|
+
}
|
|
602
|
+
save() {
|
|
603
|
+
if (typeof localStorage === `undefined`) return;
|
|
604
|
+
try {
|
|
605
|
+
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
|
|
606
|
+
} catch (e) {
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
load() {
|
|
610
|
+
if (typeof localStorage === `undefined`) return;
|
|
611
|
+
try {
|
|
612
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
613
|
+
if (stored) {
|
|
614
|
+
this.data = JSON.parse(stored);
|
|
615
|
+
}
|
|
616
|
+
} catch (e) {
|
|
617
|
+
this.data = {};
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
clear() {
|
|
621
|
+
this.data = {};
|
|
622
|
+
this.save();
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
var expiredShapesCache = new ExpiredShapesCache();
|
|
626
|
+
|
|
627
|
+
// src/snapshot-tracker.ts
|
|
628
|
+
var SnapshotTracker = class {
|
|
629
|
+
constructor() {
|
|
630
|
+
this.activeSnapshots = /* @__PURE__ */ new Map();
|
|
631
|
+
this.xmaxSnapshots = /* @__PURE__ */ new Map();
|
|
632
|
+
this.snapshotsByDatabaseLsn = /* @__PURE__ */ new Map();
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Add a new snapshot for tracking
|
|
636
|
+
*/
|
|
637
|
+
addSnapshot(metadata, keys) {
|
|
638
|
+
var _a, _b, _c, _d;
|
|
639
|
+
this.activeSnapshots.set(metadata.snapshot_mark, {
|
|
640
|
+
xmin: BigInt(metadata.xmin),
|
|
641
|
+
xmax: BigInt(metadata.xmax),
|
|
642
|
+
xip_list: metadata.xip_list.map(BigInt),
|
|
643
|
+
keys
|
|
644
|
+
});
|
|
645
|
+
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]);
|
|
646
|
+
this.xmaxSnapshots.set(BigInt(metadata.xmax), xmaxSet);
|
|
647
|
+
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]);
|
|
648
|
+
this.snapshotsByDatabaseLsn.set(
|
|
649
|
+
BigInt(metadata.database_lsn),
|
|
650
|
+
databaseLsnSet
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Remove a snapshot from tracking
|
|
655
|
+
*/
|
|
656
|
+
removeSnapshot(snapshotMark) {
|
|
657
|
+
this.activeSnapshots.delete(snapshotMark);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Check if a change message should be filtered because its already in an active snapshot
|
|
661
|
+
* Returns true if the message should be filtered out (not processed)
|
|
662
|
+
*/
|
|
663
|
+
shouldRejectMessage(message) {
|
|
664
|
+
const txids = message.headers.txids || [];
|
|
665
|
+
if (txids.length === 0) return false;
|
|
666
|
+
const xid = Math.max(...txids);
|
|
667
|
+
for (const [xmax, snapshots] of this.xmaxSnapshots.entries()) {
|
|
668
|
+
if (xid >= xmax) {
|
|
669
|
+
for (const snapshot of snapshots) {
|
|
670
|
+
this.removeSnapshot(snapshot);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return [...this.activeSnapshots.values()].some(
|
|
675
|
+
(x) => x.keys.has(message.key) && isVisibleInSnapshot(xid, x)
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
lastSeenUpdate(newDatabaseLsn) {
|
|
679
|
+
for (const [dbLsn, snapshots] of this.snapshotsByDatabaseLsn.entries()) {
|
|
680
|
+
if (dbLsn <= newDatabaseLsn) {
|
|
681
|
+
for (const snapshot of snapshots) {
|
|
682
|
+
this.removeSnapshot(snapshot);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
// src/client.ts
|
|
532
690
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
533
691
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
534
692
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
@@ -573,9 +731,18 @@ function resolveHeaders(headers) {
|
|
|
573
731
|
return Object.fromEntries(resolvedEntries);
|
|
574
732
|
});
|
|
575
733
|
}
|
|
576
|
-
|
|
734
|
+
function canonicalShapeKey(url) {
|
|
735
|
+
const cleanUrl = new URL(url.origin + url.pathname);
|
|
736
|
+
for (const [key, value] of url.searchParams) {
|
|
737
|
+
if (!ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) {
|
|
738
|
+
cleanUrl.searchParams.set(key, value);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
cleanUrl.searchParams.sort();
|
|
742
|
+
return cleanUrl.toString();
|
|
743
|
+
}
|
|
744
|
+
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;
|
|
577
745
|
var ShapeStream = class {
|
|
578
|
-
// promise chain for incoming messages
|
|
579
746
|
constructor(options) {
|
|
580
747
|
__privateAdd(this, _ShapeStream_instances);
|
|
581
748
|
__privateAdd(this, _error, null);
|
|
@@ -591,8 +758,10 @@ var ShapeStream = class {
|
|
|
591
758
|
__privateAdd(this, _lastSyncedAt);
|
|
592
759
|
// unix time
|
|
593
760
|
__privateAdd(this, _isUpToDate, false);
|
|
761
|
+
__privateAdd(this, _isMidStream, true);
|
|
594
762
|
__privateAdd(this, _connected, false);
|
|
595
763
|
__privateAdd(this, _shapeHandle);
|
|
764
|
+
__privateAdd(this, _mode);
|
|
596
765
|
__privateAdd(this, _schema);
|
|
597
766
|
__privateAdd(this, _onError);
|
|
598
767
|
__privateAdd(this, _requestAbortController);
|
|
@@ -601,16 +770,26 @@ var ShapeStream = class {
|
|
|
601
770
|
__privateAdd(this, _tickPromiseResolver);
|
|
602
771
|
__privateAdd(this, _tickPromiseRejecter);
|
|
603
772
|
__privateAdd(this, _messageChain, Promise.resolve([]));
|
|
604
|
-
|
|
773
|
+
// promise chain for incoming messages
|
|
774
|
+
__privateAdd(this, _snapshotTracker, new SnapshotTracker());
|
|
775
|
+
__privateAdd(this, _activeSnapshotRequests, 0);
|
|
776
|
+
// counter for concurrent snapshot requests
|
|
777
|
+
__privateAdd(this, _midStreamPromise);
|
|
778
|
+
__privateAdd(this, _midStreamPromiseResolver);
|
|
779
|
+
var _a, _b, _c, _d;
|
|
605
780
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
606
781
|
validateOptions(this.options);
|
|
607
782
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
608
783
|
__privateSet(this, _liveCacheBuster, ``);
|
|
609
784
|
__privateSet(this, _shapeHandle, this.options.handle);
|
|
610
|
-
__privateSet(this, _messageParser, new MessageParser(
|
|
785
|
+
__privateSet(this, _messageParser, new MessageParser(
|
|
786
|
+
options.parser,
|
|
787
|
+
options.transformer
|
|
788
|
+
));
|
|
611
789
|
__privateSet(this, _onError, this.options.onError);
|
|
612
|
-
|
|
613
|
-
const
|
|
790
|
+
__privateSet(this, _mode, (_b = this.options.mode) != null ? _b : `full`);
|
|
791
|
+
const baseFetchClient = (_c = options.fetchClient) != null ? _c : (...args) => fetch(...args);
|
|
792
|
+
const backOffOpts = __spreadProps(__spreadValues({}, (_d = options.backoffOptions) != null ? _d : BackoffDefaults), {
|
|
614
793
|
onFailedAttempt: () => {
|
|
615
794
|
var _a2, _b2;
|
|
616
795
|
__privateSet(this, _connected, false);
|
|
@@ -639,6 +818,9 @@ var ShapeStream = class {
|
|
|
639
818
|
get lastOffset() {
|
|
640
819
|
return __privateGet(this, _lastOffset);
|
|
641
820
|
}
|
|
821
|
+
get mode() {
|
|
822
|
+
return __privateGet(this, _mode);
|
|
823
|
+
}
|
|
642
824
|
subscribe(callback, onError = () => {
|
|
643
825
|
}) {
|
|
644
826
|
const subscriptionId = Math.random();
|
|
@@ -691,6 +873,56 @@ var ShapeStream = class {
|
|
|
691
873
|
__privateSet(this, _isRefreshing, false);
|
|
692
874
|
});
|
|
693
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Request a snapshot for subset of data.
|
|
878
|
+
*
|
|
879
|
+
* Only available when mode is `changes_only`.
|
|
880
|
+
* Returns the insertion point & the data, but more importantly injects the data
|
|
881
|
+
* into the subscribed data stream. Returned value is unlikely to be useful for the caller,
|
|
882
|
+
* unless the caller has complicated additional logic.
|
|
883
|
+
*
|
|
884
|
+
* Data will be injected in a way that's also tracking further incoming changes, and it'll
|
|
885
|
+
* skip the ones that are already in the snapshot.
|
|
886
|
+
*
|
|
887
|
+
* @param opts - The options for the snapshot request.
|
|
888
|
+
* @returns The metadata and the data for the snapshot.
|
|
889
|
+
*/
|
|
890
|
+
requestSnapshot(opts) {
|
|
891
|
+
return __async(this, null, function* () {
|
|
892
|
+
if (__privateGet(this, _mode) === `full`) {
|
|
893
|
+
throw new Error(
|
|
894
|
+
`Snapshot requests are not supported in ${__privateGet(this, _mode)} mode, as the consumer is guaranteed to observe all data`
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
if (!__privateGet(this, _started)) yield __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
898
|
+
yield __privateMethod(this, _ShapeStream_instances, waitForStreamEnd_fn).call(this);
|
|
899
|
+
__privateWrapper(this, _activeSnapshotRequests)._++;
|
|
900
|
+
try {
|
|
901
|
+
if (__privateGet(this, _activeSnapshotRequests) === 1) {
|
|
902
|
+
__privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
|
|
903
|
+
}
|
|
904
|
+
const { fetchUrl, requestHeaders } = yield __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, this.options.url, true, opts);
|
|
905
|
+
const { metadata, data } = yield __privateMethod(this, _ShapeStream_instances, fetchSnapshot_fn).call(this, fetchUrl, requestHeaders);
|
|
906
|
+
const dataWithEndBoundary = data.concat([
|
|
907
|
+
{ headers: __spreadValues({ control: `snapshot-end` }, metadata) }
|
|
908
|
+
]);
|
|
909
|
+
__privateGet(this, _snapshotTracker).addSnapshot(
|
|
910
|
+
metadata,
|
|
911
|
+
new Set(data.map((message) => message.key))
|
|
912
|
+
);
|
|
913
|
+
__privateMethod(this, _ShapeStream_instances, onMessages_fn).call(this, dataWithEndBoundary, false);
|
|
914
|
+
return {
|
|
915
|
+
metadata,
|
|
916
|
+
data
|
|
917
|
+
};
|
|
918
|
+
} finally {
|
|
919
|
+
__privateWrapper(this, _activeSnapshotRequests)._--;
|
|
920
|
+
if (__privateGet(this, _activeSnapshotRequests) === 0) {
|
|
921
|
+
__privateMethod(this, _ShapeStream_instances, resume_fn).call(this);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
}
|
|
694
926
|
};
|
|
695
927
|
_error = new WeakMap();
|
|
696
928
|
_fetchClient2 = new WeakMap();
|
|
@@ -703,8 +935,10 @@ _lastOffset = new WeakMap();
|
|
|
703
935
|
_liveCacheBuster = new WeakMap();
|
|
704
936
|
_lastSyncedAt = new WeakMap();
|
|
705
937
|
_isUpToDate = new WeakMap();
|
|
938
|
+
_isMidStream = new WeakMap();
|
|
706
939
|
_connected = new WeakMap();
|
|
707
940
|
_shapeHandle = new WeakMap();
|
|
941
|
+
_mode = new WeakMap();
|
|
708
942
|
_schema = new WeakMap();
|
|
709
943
|
_onError = new WeakMap();
|
|
710
944
|
_requestAbortController = new WeakMap();
|
|
@@ -713,6 +947,10 @@ _tickPromise = new WeakMap();
|
|
|
713
947
|
_tickPromiseResolver = new WeakMap();
|
|
714
948
|
_tickPromiseRejecter = new WeakMap();
|
|
715
949
|
_messageChain = new WeakMap();
|
|
950
|
+
_snapshotTracker = new WeakMap();
|
|
951
|
+
_activeSnapshotRequests = new WeakMap();
|
|
952
|
+
_midStreamPromise = new WeakMap();
|
|
953
|
+
_midStreamPromiseResolver = new WeakMap();
|
|
716
954
|
_ShapeStream_instances = new WeakSet();
|
|
717
955
|
start_fn = function() {
|
|
718
956
|
return __async(this, null, function* () {
|
|
@@ -779,6 +1017,10 @@ requestShape_fn = function() {
|
|
|
779
1017
|
}
|
|
780
1018
|
if (!(e instanceof FetchError)) throw e;
|
|
781
1019
|
if (e.status == 409) {
|
|
1020
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
1021
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1022
|
+
expiredShapesCache.markExpired(shapeKey, __privateGet(this, _shapeHandle));
|
|
1023
|
+
}
|
|
782
1024
|
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this, _shapeHandle)}-next`;
|
|
783
1025
|
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
784
1026
|
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
@@ -797,15 +1039,13 @@ requestShape_fn = function() {
|
|
|
797
1039
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
798
1040
|
});
|
|
799
1041
|
};
|
|
800
|
-
constructUrl_fn = function(url, resumingFromPause) {
|
|
1042
|
+
constructUrl_fn = function(url, resumingFromPause, subsetParams) {
|
|
801
1043
|
return __async(this, null, function* () {
|
|
802
1044
|
const [requestHeaders, params] = yield Promise.all([
|
|
803
1045
|
resolveHeaders(this.options.headers),
|
|
804
1046
|
this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
|
|
805
1047
|
]);
|
|
806
|
-
if (params)
|
|
807
|
-
validateParams(params);
|
|
808
|
-
}
|
|
1048
|
+
if (params) validateParams(params);
|
|
809
1049
|
const fetchUrl = new URL(url);
|
|
810
1050
|
if (params) {
|
|
811
1051
|
if (params.table) setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table);
|
|
@@ -825,7 +1065,20 @@ constructUrl_fn = function(url, resumingFromPause) {
|
|
|
825
1065
|
setQueryParam(fetchUrl, key, value);
|
|
826
1066
|
}
|
|
827
1067
|
}
|
|
1068
|
+
if (subsetParams) {
|
|
1069
|
+
if (subsetParams.where)
|
|
1070
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, subsetParams.where);
|
|
1071
|
+
if (subsetParams.params)
|
|
1072
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_WHERE_PARAMS, subsetParams.params);
|
|
1073
|
+
if (subsetParams.limit)
|
|
1074
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_LIMIT, subsetParams.limit);
|
|
1075
|
+
if (subsetParams.offset)
|
|
1076
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_OFFSET, subsetParams.offset);
|
|
1077
|
+
if (subsetParams.orderBy)
|
|
1078
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, subsetParams.orderBy);
|
|
1079
|
+
}
|
|
828
1080
|
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
1081
|
+
fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, __privateGet(this, _mode));
|
|
829
1082
|
if (__privateGet(this, _isUpToDate)) {
|
|
830
1083
|
if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
|
|
831
1084
|
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
@@ -838,6 +1091,11 @@ constructUrl_fn = function(url, resumingFromPause) {
|
|
|
838
1091
|
if (__privateGet(this, _shapeHandle)) {
|
|
839
1092
|
fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
|
|
840
1093
|
}
|
|
1094
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1095
|
+
const expiredHandle = expiredShapesCache.getExpiredHandle(shapeKey);
|
|
1096
|
+
if (expiredHandle) {
|
|
1097
|
+
fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle);
|
|
1098
|
+
}
|
|
841
1099
|
fetchUrl.searchParams.sort();
|
|
842
1100
|
return {
|
|
843
1101
|
fetchUrl,
|
|
@@ -890,7 +1148,9 @@ onInitialResponse_fn = function(response) {
|
|
|
890
1148
|
};
|
|
891
1149
|
onMessages_fn = function(batch, isSseMessage = false) {
|
|
892
1150
|
return __async(this, null, function* () {
|
|
1151
|
+
var _a;
|
|
893
1152
|
if (batch.length > 0) {
|
|
1153
|
+
__privateSet(this, _isMidStream, true);
|
|
894
1154
|
const lastMessage = batch[batch.length - 1];
|
|
895
1155
|
if (isUpToDateMessage(lastMessage)) {
|
|
896
1156
|
if (isSseMessage) {
|
|
@@ -901,8 +1161,16 @@ onMessages_fn = function(batch, isSseMessage = false) {
|
|
|
901
1161
|
}
|
|
902
1162
|
__privateSet(this, _lastSyncedAt, Date.now());
|
|
903
1163
|
__privateSet(this, _isUpToDate, true);
|
|
1164
|
+
__privateSet(this, _isMidStream, false);
|
|
1165
|
+
(_a = __privateGet(this, _midStreamPromiseResolver)) == null ? void 0 : _a.call(this);
|
|
904
1166
|
}
|
|
905
|
-
|
|
1167
|
+
const messagesToProcess = batch.filter((message) => {
|
|
1168
|
+
if (isChangeMessage(message)) {
|
|
1169
|
+
return !__privateGet(this, _snapshotTracker).shouldRejectMessage(message);
|
|
1170
|
+
}
|
|
1171
|
+
return true;
|
|
1172
|
+
});
|
|
1173
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messagesToProcess);
|
|
906
1174
|
}
|
|
907
1175
|
});
|
|
908
1176
|
};
|
|
@@ -1000,6 +1268,24 @@ nextTick_fn = function() {
|
|
|
1000
1268
|
return __privateGet(this, _tickPromise);
|
|
1001
1269
|
});
|
|
1002
1270
|
};
|
|
1271
|
+
waitForStreamEnd_fn = function() {
|
|
1272
|
+
return __async(this, null, function* () {
|
|
1273
|
+
if (!__privateGet(this, _isMidStream)) {
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
if (__privateGet(this, _midStreamPromise)) {
|
|
1277
|
+
return __privateGet(this, _midStreamPromise);
|
|
1278
|
+
}
|
|
1279
|
+
__privateSet(this, _midStreamPromise, new Promise((resolve) => {
|
|
1280
|
+
__privateSet(this, _midStreamPromiseResolver, resolve);
|
|
1281
|
+
}));
|
|
1282
|
+
__privateGet(this, _midStreamPromise).finally(() => {
|
|
1283
|
+
__privateSet(this, _midStreamPromise, void 0);
|
|
1284
|
+
__privateSet(this, _midStreamPromiseResolver, void 0);
|
|
1285
|
+
});
|
|
1286
|
+
return __privateGet(this, _midStreamPromise);
|
|
1287
|
+
});
|
|
1288
|
+
};
|
|
1003
1289
|
publish_fn = function(messages) {
|
|
1004
1290
|
return __async(this, null, function* () {
|
|
1005
1291
|
__privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
|
|
@@ -1044,8 +1330,33 @@ reset_fn = function(handle) {
|
|
|
1044
1330
|
__privateSet(this, _liveCacheBuster, ``);
|
|
1045
1331
|
__privateSet(this, _shapeHandle, handle);
|
|
1046
1332
|
__privateSet(this, _isUpToDate, false);
|
|
1333
|
+
__privateSet(this, _isMidStream, true);
|
|
1047
1334
|
__privateSet(this, _connected, false);
|
|
1048
1335
|
__privateSet(this, _schema, void 0);
|
|
1336
|
+
__privateSet(this, _activeSnapshotRequests, 0);
|
|
1337
|
+
};
|
|
1338
|
+
fetchSnapshot_fn = function(url, headers) {
|
|
1339
|
+
return __async(this, null, function* () {
|
|
1340
|
+
const response = yield __privateGet(this, _fetchClient2).call(this, url.toString(), { headers });
|
|
1341
|
+
if (!response.ok) {
|
|
1342
|
+
throw new FetchError(
|
|
1343
|
+
response.status,
|
|
1344
|
+
void 0,
|
|
1345
|
+
void 0,
|
|
1346
|
+
Object.fromEntries([...response.headers.entries()]),
|
|
1347
|
+
url.toString()
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
const { metadata, data } = yield response.json();
|
|
1351
|
+
const batch = __privateGet(this, _messageParser).parse(
|
|
1352
|
+
JSON.stringify(data),
|
|
1353
|
+
__privateGet(this, _schema)
|
|
1354
|
+
);
|
|
1355
|
+
return {
|
|
1356
|
+
metadata,
|
|
1357
|
+
data: batch
|
|
1358
|
+
};
|
|
1359
|
+
});
|
|
1049
1360
|
};
|
|
1050
1361
|
ShapeStream.Replica = {
|
|
1051
1362
|
FULL: `full`,
|
|
@@ -1067,7 +1378,7 @@ function validateOptions(options) {
|
|
|
1067
1378
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
1068
1379
|
throw new InvalidSignalError();
|
|
1069
1380
|
}
|
|
1070
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
1381
|
+
if (options.offset !== void 0 && options.offset !== `-1` && options.offset !== `now` && !options.handle) {
|
|
1071
1382
|
throw new MissingShapeHandleError();
|
|
1072
1383
|
}
|
|
1073
1384
|
validateParams(options.params);
|
|
@@ -1096,12 +1407,15 @@ function convertWhereParamsToObj(allPgParams) {
|
|
|
1096
1407
|
}
|
|
1097
1408
|
|
|
1098
1409
|
// src/shape.ts
|
|
1099
|
-
var _data, _subscribers2, _status, _error2, _Shape_instances, process_fn, updateShapeStatus_fn, handleError_fn, notify_fn;
|
|
1410
|
+
var _data, _subscribers2, _insertedKeys, _requestedSubSnapshots, _reexecuteSnapshotsPending, _status, _error2, _Shape_instances, process_fn, reexecuteSnapshots_fn, awaitUpToDate_fn, updateShapeStatus_fn, handleError_fn, notify_fn;
|
|
1100
1411
|
var Shape = class {
|
|
1101
1412
|
constructor(stream) {
|
|
1102
1413
|
__privateAdd(this, _Shape_instances);
|
|
1103
1414
|
__privateAdd(this, _data, /* @__PURE__ */ new Map());
|
|
1104
1415
|
__privateAdd(this, _subscribers2, /* @__PURE__ */ new Map());
|
|
1416
|
+
__privateAdd(this, _insertedKeys, /* @__PURE__ */ new Set());
|
|
1417
|
+
__privateAdd(this, _requestedSubSnapshots, /* @__PURE__ */ new Set());
|
|
1418
|
+
__privateAdd(this, _reexecuteSnapshotsPending, false);
|
|
1105
1419
|
__privateAdd(this, _status, `syncing`);
|
|
1106
1420
|
__privateAdd(this, _error2, false);
|
|
1107
1421
|
this.stream = stream;
|
|
@@ -1160,6 +1474,22 @@ var Shape = class {
|
|
|
1160
1474
|
isConnected() {
|
|
1161
1475
|
return this.stream.isConnected();
|
|
1162
1476
|
}
|
|
1477
|
+
/** Current log mode of the underlying stream */
|
|
1478
|
+
get mode() {
|
|
1479
|
+
return this.stream.mode;
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Request a snapshot for subset of data. Only available when mode is changes_only.
|
|
1483
|
+
* Returns void; data will be emitted via the stream and processed by this Shape.
|
|
1484
|
+
*/
|
|
1485
|
+
requestSnapshot(params) {
|
|
1486
|
+
return __async(this, null, function* () {
|
|
1487
|
+
const key = JSON.stringify(params);
|
|
1488
|
+
__privateGet(this, _requestedSubSnapshots).add(key);
|
|
1489
|
+
yield __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
|
|
1490
|
+
yield this.stream.requestSnapshot(params);
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1163
1493
|
subscribe(callback) {
|
|
1164
1494
|
const subscriptionId = Math.random();
|
|
1165
1495
|
__privateGet(this, _subscribers2).set(subscriptionId, callback);
|
|
@@ -1176,6 +1506,9 @@ var Shape = class {
|
|
|
1176
1506
|
};
|
|
1177
1507
|
_data = new WeakMap();
|
|
1178
1508
|
_subscribers2 = new WeakMap();
|
|
1509
|
+
_insertedKeys = new WeakMap();
|
|
1510
|
+
_requestedSubSnapshots = new WeakMap();
|
|
1511
|
+
_reexecuteSnapshotsPending = new WeakMap();
|
|
1179
1512
|
_status = new WeakMap();
|
|
1180
1513
|
_error2 = new WeakMap();
|
|
1181
1514
|
_Shape_instances = new WeakSet();
|
|
@@ -1184,33 +1517,93 @@ process_fn = function(messages) {
|
|
|
1184
1517
|
messages.forEach((message) => {
|
|
1185
1518
|
if (isChangeMessage(message)) {
|
|
1186
1519
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1520
|
+
if (this.mode === `full`) {
|
|
1521
|
+
switch (message.headers.operation) {
|
|
1522
|
+
case `insert`:
|
|
1523
|
+
__privateGet(this, _data).set(message.key, message.value);
|
|
1524
|
+
break;
|
|
1525
|
+
case `update`:
|
|
1526
|
+
__privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
|
|
1527
|
+
break;
|
|
1528
|
+
case `delete`:
|
|
1529
|
+
__privateGet(this, _data).delete(message.key);
|
|
1530
|
+
break;
|
|
1531
|
+
}
|
|
1532
|
+
} else {
|
|
1533
|
+
switch (message.headers.operation) {
|
|
1534
|
+
case `insert`:
|
|
1535
|
+
__privateGet(this, _insertedKeys).add(message.key);
|
|
1536
|
+
__privateGet(this, _data).set(message.key, message.value);
|
|
1537
|
+
break;
|
|
1538
|
+
case `update`:
|
|
1539
|
+
if (__privateGet(this, _insertedKeys).has(message.key)) {
|
|
1540
|
+
__privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
|
|
1541
|
+
}
|
|
1542
|
+
break;
|
|
1543
|
+
case `delete`:
|
|
1544
|
+
if (__privateGet(this, _insertedKeys).has(message.key)) {
|
|
1545
|
+
__privateGet(this, _data).delete(message.key);
|
|
1546
|
+
__privateGet(this, _insertedKeys).delete(message.key);
|
|
1547
|
+
}
|
|
1548
|
+
break;
|
|
1549
|
+
}
|
|
1197
1550
|
}
|
|
1198
1551
|
}
|
|
1199
1552
|
if (isControlMessage(message)) {
|
|
1200
1553
|
switch (message.headers.control) {
|
|
1201
1554
|
case `up-to-date`:
|
|
1202
1555
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `up-to-date`);
|
|
1556
|
+
if (__privateGet(this, _reexecuteSnapshotsPending)) {
|
|
1557
|
+
__privateSet(this, _reexecuteSnapshotsPending, false);
|
|
1558
|
+
void __privateMethod(this, _Shape_instances, reexecuteSnapshots_fn).call(this);
|
|
1559
|
+
}
|
|
1203
1560
|
break;
|
|
1204
1561
|
case `must-refetch`:
|
|
1205
1562
|
__privateGet(this, _data).clear();
|
|
1563
|
+
__privateGet(this, _insertedKeys).clear();
|
|
1206
1564
|
__privateSet(this, _error2, false);
|
|
1207
1565
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
|
|
1566
|
+
__privateSet(this, _reexecuteSnapshotsPending, true);
|
|
1208
1567
|
break;
|
|
1209
1568
|
}
|
|
1210
1569
|
}
|
|
1211
1570
|
});
|
|
1212
1571
|
if (shouldNotify) __privateMethod(this, _Shape_instances, notify_fn).call(this);
|
|
1213
1572
|
};
|
|
1573
|
+
reexecuteSnapshots_fn = function() {
|
|
1574
|
+
return __async(this, null, function* () {
|
|
1575
|
+
yield __privateMethod(this, _Shape_instances, awaitUpToDate_fn).call(this);
|
|
1576
|
+
yield Promise.all(
|
|
1577
|
+
Array.from(__privateGet(this, _requestedSubSnapshots)).map((jsonParams) => __async(this, null, function* () {
|
|
1578
|
+
try {
|
|
1579
|
+
const snapshot = JSON.parse(jsonParams);
|
|
1580
|
+
yield this.stream.requestSnapshot(snapshot);
|
|
1581
|
+
} catch (_) {
|
|
1582
|
+
}
|
|
1583
|
+
}))
|
|
1584
|
+
);
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1587
|
+
awaitUpToDate_fn = function() {
|
|
1588
|
+
return __async(this, null, function* () {
|
|
1589
|
+
if (this.stream.isUpToDate) return;
|
|
1590
|
+
yield new Promise((resolve) => {
|
|
1591
|
+
const check = () => {
|
|
1592
|
+
if (this.stream.isUpToDate) {
|
|
1593
|
+
clearInterval(interval);
|
|
1594
|
+
unsub();
|
|
1595
|
+
resolve();
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
const interval = setInterval(check, 10);
|
|
1599
|
+
const unsub = this.stream.subscribe(
|
|
1600
|
+
() => check(),
|
|
1601
|
+
() => check()
|
|
1602
|
+
);
|
|
1603
|
+
check();
|
|
1604
|
+
});
|
|
1605
|
+
});
|
|
1606
|
+
};
|
|
1214
1607
|
updateShapeStatus_fn = function(status) {
|
|
1215
1608
|
const stateChanged = __privateGet(this, _status) !== status;
|
|
1216
1609
|
__privateSet(this, _status, status);
|
|
@@ -1235,6 +1628,7 @@ export {
|
|
|
1235
1628
|
ShapeStream,
|
|
1236
1629
|
isChangeMessage,
|
|
1237
1630
|
isControlMessage,
|
|
1631
|
+
isVisibleInSnapshot,
|
|
1238
1632
|
resolveValue
|
|
1239
1633
|
};
|
|
1240
1634
|
//# sourceMappingURL=index.mjs.map
|