@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/cjs/index.cjs
CHANGED
|
@@ -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);
|
|
@@ -240,8 +249,9 @@ function pgArrayParser(value, parser) {
|
|
|
240
249
|
return loop(value)[0];
|
|
241
250
|
}
|
|
242
251
|
var MessageParser = class {
|
|
243
|
-
constructor(parser) {
|
|
252
|
+
constructor(parser, transformer) {
|
|
244
253
|
this.parser = __spreadValues(__spreadValues({}, defaultParser), parser);
|
|
254
|
+
this.transformer = transformer;
|
|
245
255
|
}
|
|
246
256
|
parse(messages, schema) {
|
|
247
257
|
return JSON.parse(messages, (key, value) => {
|
|
@@ -250,6 +260,7 @@ var MessageParser = class {
|
|
|
250
260
|
Object.keys(row).forEach((key2) => {
|
|
251
261
|
row[key2] = this.parseRow(key2, row[key2], schema);
|
|
252
262
|
});
|
|
263
|
+
if (this.transformer) value = this.transformer(value);
|
|
253
264
|
}
|
|
254
265
|
return value;
|
|
255
266
|
});
|
|
@@ -306,6 +317,13 @@ function getOffset(message) {
|
|
|
306
317
|
}
|
|
307
318
|
return `${lsn}_0`;
|
|
308
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
|
+
}
|
|
309
327
|
|
|
310
328
|
// src/constants.ts
|
|
311
329
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
@@ -315,6 +333,7 @@ var SHAPE_SCHEMA_HEADER = `electric-schema`;
|
|
|
315
333
|
var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
|
|
316
334
|
var COLUMNS_QUERY_PARAM = `columns`;
|
|
317
335
|
var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
|
|
336
|
+
var EXPIRED_HANDLE_QUERY_PARAM = `expired_handle`;
|
|
318
337
|
var SHAPE_HANDLE_QUERY_PARAM = `handle`;
|
|
319
338
|
var LIVE_QUERY_PARAM = `live`;
|
|
320
339
|
var OFFSET_QUERY_PARAM = `offset`;
|
|
@@ -325,11 +344,24 @@ var WHERE_PARAMS_PARAM = `params`;
|
|
|
325
344
|
var EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`;
|
|
326
345
|
var FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`;
|
|
327
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`;
|
|
328
353
|
var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
329
354
|
LIVE_QUERY_PARAM,
|
|
330
355
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
331
356
|
OFFSET_QUERY_PARAM,
|
|
332
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM
|
|
357
|
+
LIVE_CACHE_BUSTER_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
|
|
333
365
|
];
|
|
334
366
|
|
|
335
367
|
// src/fetch.ts
|
|
@@ -440,10 +472,20 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
|
440
472
|
const headers = response.headers;
|
|
441
473
|
const missingHeaders = [];
|
|
442
474
|
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
443
|
-
addMissingHeaders(requiredElectricResponseHeaders);
|
|
444
475
|
const input = args[0];
|
|
445
476
|
const urlString = input.toString();
|
|
446
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);
|
|
447
489
|
if (url.searchParams.get(LIVE_QUERY_PARAM) === `true`) {
|
|
448
490
|
addMissingHeaders(requiredLiveResponseHeaders);
|
|
449
491
|
}
|
|
@@ -557,6 +599,123 @@ function noop() {
|
|
|
557
599
|
|
|
558
600
|
// src/client.ts
|
|
559
601
|
var import_fetch_event_source = require("@microsoft/fetch-event-source");
|
|
602
|
+
|
|
603
|
+
// src/expired-shapes-cache.ts
|
|
604
|
+
var ExpiredShapesCache = class {
|
|
605
|
+
constructor() {
|
|
606
|
+
this.data = {};
|
|
607
|
+
this.max = 250;
|
|
608
|
+
this.storageKey = `electric_expired_shapes`;
|
|
609
|
+
this.load();
|
|
610
|
+
}
|
|
611
|
+
getExpiredHandle(shapeUrl) {
|
|
612
|
+
const entry = this.data[shapeUrl];
|
|
613
|
+
if (entry) {
|
|
614
|
+
entry.lastUsed = Date.now();
|
|
615
|
+
this.save();
|
|
616
|
+
return entry.expiredHandle;
|
|
617
|
+
}
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
markExpired(shapeUrl, handle) {
|
|
621
|
+
this.data[shapeUrl] = { expiredHandle: handle, lastUsed: Date.now() };
|
|
622
|
+
const keys = Object.keys(this.data);
|
|
623
|
+
if (keys.length > this.max) {
|
|
624
|
+
const oldest = keys.reduce(
|
|
625
|
+
(min, k) => this.data[k].lastUsed < this.data[min].lastUsed ? k : min
|
|
626
|
+
);
|
|
627
|
+
delete this.data[oldest];
|
|
628
|
+
}
|
|
629
|
+
this.save();
|
|
630
|
+
}
|
|
631
|
+
save() {
|
|
632
|
+
if (typeof localStorage === `undefined`) return;
|
|
633
|
+
try {
|
|
634
|
+
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
|
|
635
|
+
} catch (e) {
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
load() {
|
|
639
|
+
if (typeof localStorage === `undefined`) return;
|
|
640
|
+
try {
|
|
641
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
642
|
+
if (stored) {
|
|
643
|
+
this.data = JSON.parse(stored);
|
|
644
|
+
}
|
|
645
|
+
} catch (e) {
|
|
646
|
+
this.data = {};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
clear() {
|
|
650
|
+
this.data = {};
|
|
651
|
+
this.save();
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
var expiredShapesCache = new ExpiredShapesCache();
|
|
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
|
+
|
|
718
|
+
// src/client.ts
|
|
560
719
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
561
720
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
562
721
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
@@ -601,9 +760,18 @@ function resolveHeaders(headers) {
|
|
|
601
760
|
return Object.fromEntries(resolvedEntries);
|
|
602
761
|
});
|
|
603
762
|
}
|
|
604
|
-
|
|
763
|
+
function canonicalShapeKey(url) {
|
|
764
|
+
const cleanUrl = new URL(url.origin + url.pathname);
|
|
765
|
+
for (const [key, value] of url.searchParams) {
|
|
766
|
+
if (!ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) {
|
|
767
|
+
cleanUrl.searchParams.set(key, value);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
cleanUrl.searchParams.sort();
|
|
771
|
+
return cleanUrl.toString();
|
|
772
|
+
}
|
|
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;
|
|
605
774
|
var ShapeStream = class {
|
|
606
|
-
// promise chain for incoming messages
|
|
607
775
|
constructor(options) {
|
|
608
776
|
__privateAdd(this, _ShapeStream_instances);
|
|
609
777
|
__privateAdd(this, _error, null);
|
|
@@ -619,8 +787,10 @@ var ShapeStream = class {
|
|
|
619
787
|
__privateAdd(this, _lastSyncedAt);
|
|
620
788
|
// unix time
|
|
621
789
|
__privateAdd(this, _isUpToDate, false);
|
|
790
|
+
__privateAdd(this, _isMidStream, true);
|
|
622
791
|
__privateAdd(this, _connected, false);
|
|
623
792
|
__privateAdd(this, _shapeHandle);
|
|
793
|
+
__privateAdd(this, _mode);
|
|
624
794
|
__privateAdd(this, _schema);
|
|
625
795
|
__privateAdd(this, _onError);
|
|
626
796
|
__privateAdd(this, _requestAbortController);
|
|
@@ -629,16 +799,26 @@ var ShapeStream = class {
|
|
|
629
799
|
__privateAdd(this, _tickPromiseResolver);
|
|
630
800
|
__privateAdd(this, _tickPromiseRejecter);
|
|
631
801
|
__privateAdd(this, _messageChain, Promise.resolve([]));
|
|
632
|
-
|
|
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;
|
|
633
809
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
634
810
|
validateOptions(this.options);
|
|
635
811
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
636
812
|
__privateSet(this, _liveCacheBuster, ``);
|
|
637
813
|
__privateSet(this, _shapeHandle, this.options.handle);
|
|
638
|
-
__privateSet(this, _messageParser, new MessageParser(
|
|
814
|
+
__privateSet(this, _messageParser, new MessageParser(
|
|
815
|
+
options.parser,
|
|
816
|
+
options.transformer
|
|
817
|
+
));
|
|
639
818
|
__privateSet(this, _onError, this.options.onError);
|
|
640
|
-
|
|
641
|
-
const
|
|
819
|
+
__privateSet(this, _mode, (_b = this.options.mode) != 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), {
|
|
642
822
|
onFailedAttempt: () => {
|
|
643
823
|
var _a2, _b2;
|
|
644
824
|
__privateSet(this, _connected, false);
|
|
@@ -667,6 +847,9 @@ var ShapeStream = class {
|
|
|
667
847
|
get lastOffset() {
|
|
668
848
|
return __privateGet(this, _lastOffset);
|
|
669
849
|
}
|
|
850
|
+
get mode() {
|
|
851
|
+
return __privateGet(this, _mode);
|
|
852
|
+
}
|
|
670
853
|
subscribe(callback, onError = () => {
|
|
671
854
|
}) {
|
|
672
855
|
const subscriptionId = Math.random();
|
|
@@ -719,6 +902,56 @@ var ShapeStream = class {
|
|
|
719
902
|
__privateSet(this, _isRefreshing, false);
|
|
720
903
|
});
|
|
721
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
|
+
}
|
|
722
955
|
};
|
|
723
956
|
_error = new WeakMap();
|
|
724
957
|
_fetchClient2 = new WeakMap();
|
|
@@ -731,8 +964,10 @@ _lastOffset = new WeakMap();
|
|
|
731
964
|
_liveCacheBuster = new WeakMap();
|
|
732
965
|
_lastSyncedAt = new WeakMap();
|
|
733
966
|
_isUpToDate = new WeakMap();
|
|
967
|
+
_isMidStream = new WeakMap();
|
|
734
968
|
_connected = new WeakMap();
|
|
735
969
|
_shapeHandle = new WeakMap();
|
|
970
|
+
_mode = new WeakMap();
|
|
736
971
|
_schema = new WeakMap();
|
|
737
972
|
_onError = new WeakMap();
|
|
738
973
|
_requestAbortController = new WeakMap();
|
|
@@ -741,6 +976,10 @@ _tickPromise = new WeakMap();
|
|
|
741
976
|
_tickPromiseResolver = new WeakMap();
|
|
742
977
|
_tickPromiseRejecter = new WeakMap();
|
|
743
978
|
_messageChain = new WeakMap();
|
|
979
|
+
_snapshotTracker = new WeakMap();
|
|
980
|
+
_activeSnapshotRequests = new WeakMap();
|
|
981
|
+
_midStreamPromise = new WeakMap();
|
|
982
|
+
_midStreamPromiseResolver = new WeakMap();
|
|
744
983
|
_ShapeStream_instances = new WeakSet();
|
|
745
984
|
start_fn = function() {
|
|
746
985
|
return __async(this, null, function* () {
|
|
@@ -807,6 +1046,10 @@ requestShape_fn = function() {
|
|
|
807
1046
|
}
|
|
808
1047
|
if (!(e instanceof FetchError)) throw e;
|
|
809
1048
|
if (e.status == 409) {
|
|
1049
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
1050
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1051
|
+
expiredShapesCache.markExpired(shapeKey, __privateGet(this, _shapeHandle));
|
|
1052
|
+
}
|
|
810
1053
|
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this, _shapeHandle)}-next`;
|
|
811
1054
|
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
812
1055
|
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
@@ -825,15 +1068,13 @@ requestShape_fn = function() {
|
|
|
825
1068
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
826
1069
|
});
|
|
827
1070
|
};
|
|
828
|
-
constructUrl_fn = function(url, resumingFromPause) {
|
|
1071
|
+
constructUrl_fn = function(url, resumingFromPause, subsetParams) {
|
|
829
1072
|
return __async(this, null, function* () {
|
|
830
1073
|
const [requestHeaders, params] = yield Promise.all([
|
|
831
1074
|
resolveHeaders(this.options.headers),
|
|
832
1075
|
this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
|
|
833
1076
|
]);
|
|
834
|
-
if (params)
|
|
835
|
-
validateParams(params);
|
|
836
|
-
}
|
|
1077
|
+
if (params) validateParams(params);
|
|
837
1078
|
const fetchUrl = new URL(url);
|
|
838
1079
|
if (params) {
|
|
839
1080
|
if (params.table) setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table);
|
|
@@ -853,7 +1094,20 @@ constructUrl_fn = function(url, resumingFromPause) {
|
|
|
853
1094
|
setQueryParam(fetchUrl, key, value);
|
|
854
1095
|
}
|
|
855
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
|
+
}
|
|
856
1109
|
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
1110
|
+
fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, __privateGet(this, _mode));
|
|
857
1111
|
if (__privateGet(this, _isUpToDate)) {
|
|
858
1112
|
if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
|
|
859
1113
|
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
@@ -866,6 +1120,11 @@ constructUrl_fn = function(url, resumingFromPause) {
|
|
|
866
1120
|
if (__privateGet(this, _shapeHandle)) {
|
|
867
1121
|
fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
|
|
868
1122
|
}
|
|
1123
|
+
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1124
|
+
const expiredHandle = expiredShapesCache.getExpiredHandle(shapeKey);
|
|
1125
|
+
if (expiredHandle) {
|
|
1126
|
+
fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle);
|
|
1127
|
+
}
|
|
869
1128
|
fetchUrl.searchParams.sort();
|
|
870
1129
|
return {
|
|
871
1130
|
fetchUrl,
|
|
@@ -918,7 +1177,9 @@ onInitialResponse_fn = function(response) {
|
|
|
918
1177
|
};
|
|
919
1178
|
onMessages_fn = function(batch, isSseMessage = false) {
|
|
920
1179
|
return __async(this, null, function* () {
|
|
1180
|
+
var _a;
|
|
921
1181
|
if (batch.length > 0) {
|
|
1182
|
+
__privateSet(this, _isMidStream, true);
|
|
922
1183
|
const lastMessage = batch[batch.length - 1];
|
|
923
1184
|
if (isUpToDateMessage(lastMessage)) {
|
|
924
1185
|
if (isSseMessage) {
|
|
@@ -929,8 +1190,16 @@ onMessages_fn = function(batch, isSseMessage = false) {
|
|
|
929
1190
|
}
|
|
930
1191
|
__privateSet(this, _lastSyncedAt, Date.now());
|
|
931
1192
|
__privateSet(this, _isUpToDate, true);
|
|
1193
|
+
__privateSet(this, _isMidStream, false);
|
|
1194
|
+
(_a = __privateGet(this, _midStreamPromiseResolver)) == null ? void 0 : _a.call(this);
|
|
932
1195
|
}
|
|
933
|
-
|
|
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);
|
|
934
1203
|
}
|
|
935
1204
|
});
|
|
936
1205
|
};
|
|
@@ -1028,6 +1297,24 @@ nextTick_fn = function() {
|
|
|
1028
1297
|
return __privateGet(this, _tickPromise);
|
|
1029
1298
|
});
|
|
1030
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
|
+
};
|
|
1031
1318
|
publish_fn = function(messages) {
|
|
1032
1319
|
return __async(this, null, function* () {
|
|
1033
1320
|
__privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
|
|
@@ -1072,8 +1359,33 @@ reset_fn = function(handle) {
|
|
|
1072
1359
|
__privateSet(this, _liveCacheBuster, ``);
|
|
1073
1360
|
__privateSet(this, _shapeHandle, handle);
|
|
1074
1361
|
__privateSet(this, _isUpToDate, false);
|
|
1362
|
+
__privateSet(this, _isMidStream, true);
|
|
1075
1363
|
__privateSet(this, _connected, false);
|
|
1076
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
|
+
});
|
|
1077
1389
|
};
|
|
1078
1390
|
ShapeStream.Replica = {
|
|
1079
1391
|
FULL: `full`,
|
|
@@ -1095,7 +1407,7 @@ function validateOptions(options) {
|
|
|
1095
1407
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
1096
1408
|
throw new InvalidSignalError();
|
|
1097
1409
|
}
|
|
1098
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
1410
|
+
if (options.offset !== void 0 && options.offset !== `-1` && options.offset !== `now` && !options.handle) {
|
|
1099
1411
|
throw new MissingShapeHandleError();
|
|
1100
1412
|
}
|
|
1101
1413
|
validateParams(options.params);
|
|
@@ -1124,12 +1436,15 @@ function convertWhereParamsToObj(allPgParams) {
|
|
|
1124
1436
|
}
|
|
1125
1437
|
|
|
1126
1438
|
// src/shape.ts
|
|
1127
|
-
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;
|
|
1128
1440
|
var Shape = class {
|
|
1129
1441
|
constructor(stream) {
|
|
1130
1442
|
__privateAdd(this, _Shape_instances);
|
|
1131
1443
|
__privateAdd(this, _data, /* @__PURE__ */ new Map());
|
|
1132
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);
|
|
1133
1448
|
__privateAdd(this, _status, `syncing`);
|
|
1134
1449
|
__privateAdd(this, _error2, false);
|
|
1135
1450
|
this.stream = stream;
|
|
@@ -1188,6 +1503,22 @@ var Shape = class {
|
|
|
1188
1503
|
isConnected() {
|
|
1189
1504
|
return this.stream.isConnected();
|
|
1190
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
|
+
}
|
|
1191
1522
|
subscribe(callback) {
|
|
1192
1523
|
const subscriptionId = Math.random();
|
|
1193
1524
|
__privateGet(this, _subscribers2).set(subscriptionId, callback);
|
|
@@ -1204,6 +1535,9 @@ var Shape = class {
|
|
|
1204
1535
|
};
|
|
1205
1536
|
_data = new WeakMap();
|
|
1206
1537
|
_subscribers2 = new WeakMap();
|
|
1538
|
+
_insertedKeys = new WeakMap();
|
|
1539
|
+
_requestedSubSnapshots = new WeakMap();
|
|
1540
|
+
_reexecuteSnapshotsPending = new WeakMap();
|
|
1207
1541
|
_status = new WeakMap();
|
|
1208
1542
|
_error2 = new WeakMap();
|
|
1209
1543
|
_Shape_instances = new WeakSet();
|
|
@@ -1212,33 +1546,93 @@ process_fn = function(messages) {
|
|
|
1212
1546
|
messages.forEach((message) => {
|
|
1213
1547
|
if (isChangeMessage(message)) {
|
|
1214
1548
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
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
|
+
}
|
|
1225
1579
|
}
|
|
1226
1580
|
}
|
|
1227
1581
|
if (isControlMessage(message)) {
|
|
1228
1582
|
switch (message.headers.control) {
|
|
1229
1583
|
case `up-to-date`:
|
|
1230
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
|
+
}
|
|
1231
1589
|
break;
|
|
1232
1590
|
case `must-refetch`:
|
|
1233
1591
|
__privateGet(this, _data).clear();
|
|
1592
|
+
__privateGet(this, _insertedKeys).clear();
|
|
1234
1593
|
__privateSet(this, _error2, false);
|
|
1235
1594
|
shouldNotify = __privateMethod(this, _Shape_instances, updateShapeStatus_fn).call(this, `syncing`);
|
|
1595
|
+
__privateSet(this, _reexecuteSnapshotsPending, true);
|
|
1236
1596
|
break;
|
|
1237
1597
|
}
|
|
1238
1598
|
}
|
|
1239
1599
|
});
|
|
1240
1600
|
if (shouldNotify) __privateMethod(this, _Shape_instances, notify_fn).call(this);
|
|
1241
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
|
+
};
|
|
1242
1636
|
updateShapeStatus_fn = function(status) {
|
|
1243
1637
|
const stateChanged = __privateGet(this, _status) !== status;
|
|
1244
1638
|
__privateSet(this, _status, status);
|
|
@@ -1264,6 +1658,7 @@ notify_fn = function() {
|
|
|
1264
1658
|
ShapeStream,
|
|
1265
1659
|
isChangeMessage,
|
|
1266
1660
|
isControlMessage,
|
|
1661
|
+
isVisibleInSnapshot,
|
|
1267
1662
|
resolveValue
|
|
1268
1663
|
});
|
|
1269
1664
|
//# sourceMappingURL=index.cjs.map
|