@arcote.tech/arc-adapter-db-postgres 0.7.14 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +203 -256
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1484,12 +1484,13 @@ class EventWire {
|
|
|
1484
1484
|
onSyncedCallback;
|
|
1485
1485
|
reconnectTimeout;
|
|
1486
1486
|
syncRequested = false;
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
constructor(baseUrl) {
|
|
1487
|
+
querySubscriptions = new Map;
|
|
1488
|
+
querySubCounter = 0;
|
|
1489
|
+
enableEventSync;
|
|
1490
|
+
constructor(baseUrl, options) {
|
|
1491
1491
|
this.baseUrl = baseUrl;
|
|
1492
1492
|
this.instanceId = ++eventWireInstanceCounter;
|
|
1493
|
+
this.enableEventSync = options?.enableEventSync ?? true;
|
|
1493
1494
|
}
|
|
1494
1495
|
setScopeToken(scope, token) {
|
|
1495
1496
|
if (token === null) {
|
|
@@ -1535,9 +1536,11 @@ class EventWire {
|
|
|
1535
1536
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
1536
1537
|
this.state = "connected";
|
|
1537
1538
|
this.sendAllScopeTokens();
|
|
1538
|
-
this.
|
|
1539
|
+
if (this.enableEventSync) {
|
|
1540
|
+
this.requestSync();
|
|
1541
|
+
}
|
|
1539
1542
|
this.flushPendingEvents();
|
|
1540
|
-
this.
|
|
1543
|
+
this.sendAllQuerySubscriptions();
|
|
1541
1544
|
} else {
|
|
1542
1545
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
1543
1546
|
}
|
|
@@ -1613,9 +1616,13 @@ class EventWire {
|
|
|
1613
1616
|
onSynced(callback) {
|
|
1614
1617
|
this.onSyncedCallback = callback;
|
|
1615
1618
|
}
|
|
1616
|
-
subscribeQuery(descriptor,
|
|
1617
|
-
const subscriptionId = `qs_${
|
|
1618
|
-
this.
|
|
1619
|
+
subscribeQuery(descriptor, scope, callbacks) {
|
|
1620
|
+
const subscriptionId = `qs_${this.instanceId}_${++this.querySubCounter}`;
|
|
1621
|
+
this.querySubscriptions.set(subscriptionId, {
|
|
1622
|
+
descriptor,
|
|
1623
|
+
scope,
|
|
1624
|
+
callbacks
|
|
1625
|
+
});
|
|
1619
1626
|
if (this.state === "connected" && this.ws) {
|
|
1620
1627
|
this.ws.send(JSON.stringify({
|
|
1621
1628
|
type: "subscribe-query",
|
|
@@ -1623,20 +1630,17 @@ class EventWire {
|
|
|
1623
1630
|
descriptor,
|
|
1624
1631
|
scope
|
|
1625
1632
|
}));
|
|
1626
|
-
} else {
|
|
1627
|
-
this.pendingViewSubs.push({ subscriptionId, descriptor, scope });
|
|
1628
1633
|
}
|
|
1629
1634
|
return subscriptionId;
|
|
1630
1635
|
}
|
|
1631
1636
|
unsubscribeQuery(subscriptionId) {
|
|
1632
|
-
this.
|
|
1637
|
+
this.querySubscriptions.delete(subscriptionId);
|
|
1633
1638
|
if (this.state === "connected" && this.ws) {
|
|
1634
1639
|
this.ws.send(JSON.stringify({
|
|
1635
1640
|
type: "unsubscribe-query",
|
|
1636
1641
|
subscriptionId
|
|
1637
1642
|
}));
|
|
1638
1643
|
}
|
|
1639
|
-
this.pendingViewSubs = this.pendingViewSubs.filter((s) => s.subscriptionId !== subscriptionId);
|
|
1640
1644
|
}
|
|
1641
1645
|
getState() {
|
|
1642
1646
|
return this.state;
|
|
@@ -1669,10 +1673,17 @@ class EventWire {
|
|
|
1669
1673
|
this.lastHostEventId = message.lastHostEventId;
|
|
1670
1674
|
}
|
|
1671
1675
|
break;
|
|
1672
|
-
case "query-
|
|
1673
|
-
const
|
|
1674
|
-
if (
|
|
1675
|
-
|
|
1676
|
+
case "query-snapshot": {
|
|
1677
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
1678
|
+
if (sub) {
|
|
1679
|
+
sub.callbacks.onSnapshot(message.result ?? null);
|
|
1680
|
+
}
|
|
1681
|
+
break;
|
|
1682
|
+
}
|
|
1683
|
+
case "query-changes": {
|
|
1684
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
1685
|
+
if (sub && Array.isArray(message.changes)) {
|
|
1686
|
+
sub.callbacks.onChanges(message.changes);
|
|
1676
1687
|
}
|
|
1677
1688
|
break;
|
|
1678
1689
|
}
|
|
@@ -1700,18 +1711,17 @@ class EventWire {
|
|
|
1700
1711
|
this.pendingEvents = [];
|
|
1701
1712
|
}
|
|
1702
1713
|
}
|
|
1703
|
-
|
|
1714
|
+
sendAllQuerySubscriptions() {
|
|
1704
1715
|
if (!this.ws || this.state !== "connected")
|
|
1705
1716
|
return;
|
|
1706
|
-
for (const sub of this.
|
|
1717
|
+
for (const [subscriptionId, sub] of this.querySubscriptions) {
|
|
1707
1718
|
this.ws.send(JSON.stringify({
|
|
1708
1719
|
type: "subscribe-query",
|
|
1709
|
-
subscriptionId
|
|
1720
|
+
subscriptionId,
|
|
1710
1721
|
descriptor: sub.descriptor,
|
|
1711
1722
|
scope: sub.scope
|
|
1712
1723
|
}));
|
|
1713
1724
|
}
|
|
1714
|
-
this.pendingViewSubs = [];
|
|
1715
1725
|
}
|
|
1716
1726
|
scheduleReconnect() {
|
|
1717
1727
|
if (this.reconnectTimeout)
|
|
@@ -3538,6 +3548,48 @@ function deepMerge(target, source) {
|
|
|
3538
3548
|
function isPlainObject(item) {
|
|
3539
3549
|
return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
|
|
3540
3550
|
}
|
|
3551
|
+
function murmurHash(key, seed = 0) {
|
|
3552
|
+
let remainder, bytes, h1, h1b, c1, c2, k1, i;
|
|
3553
|
+
remainder = key.length & 3;
|
|
3554
|
+
bytes = key.length - remainder;
|
|
3555
|
+
h1 = seed;
|
|
3556
|
+
c1 = 3432918353;
|
|
3557
|
+
c2 = 461845907;
|
|
3558
|
+
i = 0;
|
|
3559
|
+
while (i < bytes) {
|
|
3560
|
+
k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
|
|
3561
|
+
++i;
|
|
3562
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
3563
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
3564
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
3565
|
+
h1 ^= k1;
|
|
3566
|
+
h1 = h1 << 13 | h1 >>> 19;
|
|
3567
|
+
h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
|
|
3568
|
+
h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
|
|
3569
|
+
}
|
|
3570
|
+
k1 = 0;
|
|
3571
|
+
if (remainder >= 3) {
|
|
3572
|
+
k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
|
|
3573
|
+
}
|
|
3574
|
+
if (remainder >= 2) {
|
|
3575
|
+
k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
|
|
3576
|
+
}
|
|
3577
|
+
if (remainder >= 1) {
|
|
3578
|
+
k1 ^= key.charCodeAt(i) & 255;
|
|
3579
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
3580
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
3581
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
3582
|
+
h1 ^= k1;
|
|
3583
|
+
}
|
|
3584
|
+
h1 ^= key.length;
|
|
3585
|
+
h1 ^= h1 >>> 16;
|
|
3586
|
+
h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
|
|
3587
|
+
h1 ^= h1 >>> 13;
|
|
3588
|
+
h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
|
|
3589
|
+
h1 ^= h1 >>> 16;
|
|
3590
|
+
return h1 >>> 0;
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3541
3593
|
class ForkedStoreState extends StoreState {
|
|
3542
3594
|
master;
|
|
3543
3595
|
changedItems = new Map;
|
|
@@ -3686,12 +3738,19 @@ class MasterStoreState extends StoreState {
|
|
|
3686
3738
|
constructor(storeName, dataStorage, deserialize) {
|
|
3687
3739
|
super(storeName, dataStorage, deserialize);
|
|
3688
3740
|
}
|
|
3741
|
+
async readExisting(transaction, id2, transactionCache) {
|
|
3742
|
+
const cacheKey = `${this.storeName}:${id2}`;
|
|
3743
|
+
if (transactionCache && transactionCache.has(cacheKey)) {
|
|
3744
|
+
return transactionCache.get(cacheKey);
|
|
3745
|
+
}
|
|
3746
|
+
return transaction.find(this.storeName, { where: { _id: id2 } }).then((results) => results[0]);
|
|
3747
|
+
}
|
|
3689
3748
|
async applyChangeAndReturnEvent(transaction, change, transactionCache) {
|
|
3690
3749
|
if (change.type === "set") {
|
|
3691
3750
|
await transaction.set(this.storeName, change.data);
|
|
3692
3751
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
3693
3752
|
if (transactionCache) {
|
|
3694
|
-
transactionCache.set(change.data._id
|
|
3753
|
+
transactionCache.set(`${this.storeName}:${change.data._id}`, item);
|
|
3695
3754
|
}
|
|
3696
3755
|
return {
|
|
3697
3756
|
from: null,
|
|
@@ -3705,6 +3764,9 @@ class MasterStoreState extends StoreState {
|
|
|
3705
3764
|
}
|
|
3706
3765
|
if (change.type === "delete") {
|
|
3707
3766
|
await transaction.remove(this.storeName, change.id);
|
|
3767
|
+
if (transactionCache) {
|
|
3768
|
+
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
3769
|
+
}
|
|
3708
3770
|
return {
|
|
3709
3771
|
from: null,
|
|
3710
3772
|
to: null,
|
|
@@ -3716,17 +3778,12 @@ class MasterStoreState extends StoreState {
|
|
|
3716
3778
|
};
|
|
3717
3779
|
}
|
|
3718
3780
|
if (change.type === "modify") {
|
|
3719
|
-
|
|
3720
|
-
if (transactionCache && transactionCache.has(change.id)) {
|
|
3721
|
-
existing = transactionCache.get(change.id);
|
|
3722
|
-
} else {
|
|
3723
|
-
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
3724
|
-
}
|
|
3781
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
3725
3782
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
3726
3783
|
await transaction.set(this.storeName, updated);
|
|
3727
3784
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
3728
3785
|
if (transactionCache) {
|
|
3729
|
-
transactionCache.set(change.id
|
|
3786
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
3730
3787
|
}
|
|
3731
3788
|
return {
|
|
3732
3789
|
from: null,
|
|
@@ -3739,17 +3796,12 @@ class MasterStoreState extends StoreState {
|
|
|
3739
3796
|
};
|
|
3740
3797
|
}
|
|
3741
3798
|
if (change.type === "mutate") {
|
|
3742
|
-
|
|
3743
|
-
if (transactionCache && transactionCache.has(change.id)) {
|
|
3744
|
-
existing = transactionCache.get(change.id);
|
|
3745
|
-
} else {
|
|
3746
|
-
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
3747
|
-
}
|
|
3799
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
3748
3800
|
const updated = apply(existing || {}, change.patches);
|
|
3749
3801
|
await transaction.set(this.storeName, updated);
|
|
3750
3802
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
3751
3803
|
if (transactionCache) {
|
|
3752
|
-
transactionCache.set(change.id
|
|
3804
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
3753
3805
|
}
|
|
3754
3806
|
return {
|
|
3755
3807
|
from: null,
|
|
@@ -3985,7 +4037,8 @@ class ObservableDataStorage {
|
|
|
3985
4037
|
}
|
|
3986
4038
|
handleStoreChange(storeName, events) {
|
|
3987
4039
|
let hasChanges = false;
|
|
3988
|
-
|
|
4040
|
+
const staleKeys = [];
|
|
4041
|
+
for (const [key, query] of this.trackedQueries) {
|
|
3989
4042
|
if (query.storeName !== storeName)
|
|
3990
4043
|
continue;
|
|
3991
4044
|
let currentResult = query.result;
|
|
@@ -3997,10 +4050,20 @@ class ObservableDataStorage {
|
|
|
3997
4050
|
queryChanged = true;
|
|
3998
4051
|
}
|
|
3999
4052
|
}
|
|
4000
|
-
if (queryChanged)
|
|
4001
|
-
|
|
4053
|
+
if (!queryChanged)
|
|
4054
|
+
continue;
|
|
4055
|
+
if (query.options.limit !== undefined && query.result.length === query.options.limit && currentResult.length < query.options.limit) {
|
|
4056
|
+
staleKeys.push(key);
|
|
4002
4057
|
hasChanges = true;
|
|
4058
|
+
continue;
|
|
4003
4059
|
}
|
|
4060
|
+
query.result = currentResult;
|
|
4061
|
+
hasChanges = true;
|
|
4062
|
+
}
|
|
4063
|
+
for (const key of staleKeys) {
|
|
4064
|
+
const query = this.trackedQueries.get(key);
|
|
4065
|
+
this.source.getStore(query.storeName).unsubscribe(query.listener);
|
|
4066
|
+
this.trackedQueries.delete(key);
|
|
4004
4067
|
}
|
|
4005
4068
|
if (hasChanges) {
|
|
4006
4069
|
this.onChange();
|
|
@@ -4216,13 +4279,6 @@ class ScopedModel {
|
|
|
4216
4279
|
}
|
|
4217
4280
|
return wire.query(viewName, options, this.getAuth());
|
|
4218
4281
|
}
|
|
4219
|
-
subscribeQuery(descriptor, callback) {
|
|
4220
|
-
const wire = this.parent.getAdapters().eventWire;
|
|
4221
|
-
if (!wire) {
|
|
4222
|
-
throw new Error(`Cannot subscribe to query: no eventWire available.`);
|
|
4223
|
-
}
|
|
4224
|
-
return wire.subscribeQuery(descriptor, callback, this.scopeName);
|
|
4225
|
-
}
|
|
4226
4282
|
get query() {
|
|
4227
4283
|
return buildContextAccessor(this.context, this.scopedAdapters, "queryContext", (descriptor) => descriptor);
|
|
4228
4284
|
}
|
|
@@ -4278,244 +4334,135 @@ class Model {
|
|
|
4278
4334
|
return s;
|
|
4279
4335
|
}
|
|
4280
4336
|
}
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
registerViews(views) {
|
|
4289
|
-
this.views = views;
|
|
4290
|
-
for (const view3 of views) {
|
|
4291
|
-
if (!this.stores.has(view3.name)) {
|
|
4292
|
-
this.stores.set(view3.name, new StreamingStore);
|
|
4293
|
-
}
|
|
4337
|
+
function applyQueryChanges(result, changes) {
|
|
4338
|
+
const next = [...result];
|
|
4339
|
+
for (const change of changes) {
|
|
4340
|
+
if (change.type === "delete") {
|
|
4341
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
4342
|
+
if (idx !== -1)
|
|
4343
|
+
next.splice(idx, 1);
|
|
4294
4344
|
}
|
|
4295
4345
|
}
|
|
4296
|
-
|
|
4297
|
-
if (
|
|
4298
|
-
|
|
4346
|
+
for (const change of changes) {
|
|
4347
|
+
if (change.type === "set") {
|
|
4348
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
4349
|
+
if (idx !== -1)
|
|
4350
|
+
next.splice(idx, 1);
|
|
4351
|
+
next.splice(change.index, 0, change.item);
|
|
4299
4352
|
}
|
|
4300
|
-
return this.stores.get(viewName);
|
|
4301
|
-
}
|
|
4302
|
-
hasData(viewName) {
|
|
4303
|
-
const store = this.stores.get(viewName);
|
|
4304
|
-
return store ? store.hasData() : false;
|
|
4305
|
-
}
|
|
4306
|
-
hasActiveStream(viewName) {
|
|
4307
|
-
return this.activeStreams.has(viewName);
|
|
4308
4353
|
}
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4354
|
+
return next;
|
|
4355
|
+
}
|
|
4356
|
+
class StreamingQueryCache {
|
|
4357
|
+
entries = new Map;
|
|
4358
|
+
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
4359
|
+
entryKey(descriptor, scope) {
|
|
4360
|
+
return `${scope ?? "default"}:${murmurHash(JSON.stringify(descriptor))}`;
|
|
4361
|
+
}
|
|
4362
|
+
subscribe(descriptor, scope, eventWire, onChange) {
|
|
4363
|
+
const key = this.entryKey(descriptor, scope);
|
|
4364
|
+
let entry = this.entries.get(key);
|
|
4365
|
+
if (entry) {
|
|
4366
|
+
if (entry.pendingUnsub) {
|
|
4367
|
+
clearTimeout(entry.pendingUnsub);
|
|
4368
|
+
entry.pendingUnsub = undefined;
|
|
4369
|
+
}
|
|
4370
|
+
entry.refCount++;
|
|
4371
|
+
} else {
|
|
4372
|
+
const newEntry = {
|
|
4373
|
+
result: undefined,
|
|
4374
|
+
hasResult: false,
|
|
4375
|
+
listeners: new Set,
|
|
4376
|
+
refCount: 1,
|
|
4377
|
+
subscriptionId: ""
|
|
4321
4378
|
};
|
|
4379
|
+
newEntry.subscriptionId = eventWire.subscribeQuery(descriptor, scope, {
|
|
4380
|
+
onSnapshot: (result) => {
|
|
4381
|
+
newEntry.result = result;
|
|
4382
|
+
newEntry.hasResult = true;
|
|
4383
|
+
this.notify(newEntry);
|
|
4384
|
+
},
|
|
4385
|
+
onChanges: (changes) => {
|
|
4386
|
+
if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
|
|
4387
|
+
return;
|
|
4388
|
+
}
|
|
4389
|
+
newEntry.result = applyQueryChanges(newEntry.result, changes);
|
|
4390
|
+
this.notify(newEntry);
|
|
4391
|
+
}
|
|
4392
|
+
});
|
|
4393
|
+
this.entries.set(key, newEntry);
|
|
4394
|
+
entry = newEntry;
|
|
4322
4395
|
}
|
|
4323
|
-
const
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
refCount: 1
|
|
4327
|
-
});
|
|
4396
|
+
const subscribed = entry;
|
|
4397
|
+
subscribed.listeners.add(onChange);
|
|
4398
|
+
let active = true;
|
|
4328
4399
|
return {
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
this.pendingUnsubscribes.set(viewName, timeout);
|
|
4349
|
-
}
|
|
4350
|
-
}
|
|
4351
|
-
subscribeQuery(descriptor, eventWire, scope) {
|
|
4352
|
-
const key = descriptor.element;
|
|
4353
|
-
if (scope)
|
|
4354
|
-
this.streamScopes.set(key, scope);
|
|
4355
|
-
const { unsubscribe } = this.registerStream(key, () => {
|
|
4356
|
-
const subId = eventWire.subscribeQuery(descriptor, (data) => {
|
|
4357
|
-
this.setViewData(descriptor.element, data);
|
|
4358
|
-
}, scope);
|
|
4359
|
-
return { unsubscribe: () => eventWire.unsubscribeQuery(subId) };
|
|
4360
|
-
});
|
|
4361
|
-
return unsubscribe;
|
|
4362
|
-
}
|
|
4363
|
-
invalidateScope(scope) {
|
|
4364
|
-
for (const [viewName, viewScope] of this.streamScopes) {
|
|
4365
|
-
if (viewScope !== scope)
|
|
4366
|
-
continue;
|
|
4367
|
-
const pending = this.pendingUnsubscribes.get(viewName);
|
|
4368
|
-
if (pending) {
|
|
4369
|
-
clearTimeout(pending);
|
|
4370
|
-
this.pendingUnsubscribes.delete(viewName);
|
|
4400
|
+
read: () => ({
|
|
4401
|
+
result: subscribed.result,
|
|
4402
|
+
loading: !subscribed.hasResult
|
|
4403
|
+
}),
|
|
4404
|
+
unsubscribe: () => {
|
|
4405
|
+
if (!active)
|
|
4406
|
+
return;
|
|
4407
|
+
active = false;
|
|
4408
|
+
subscribed.listeners.delete(onChange);
|
|
4409
|
+
subscribed.refCount--;
|
|
4410
|
+
if (subscribed.refCount > 0)
|
|
4411
|
+
return;
|
|
4412
|
+
subscribed.pendingUnsub = setTimeout(() => {
|
|
4413
|
+
subscribed.pendingUnsub = undefined;
|
|
4414
|
+
if (subscribed.refCount > 0)
|
|
4415
|
+
return;
|
|
4416
|
+
eventWire.unsubscribeQuery(subscribed.subscriptionId);
|
|
4417
|
+
this.entries.delete(key);
|
|
4418
|
+
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
4371
4419
|
}
|
|
4372
|
-
|
|
4373
|
-
if (stream) {
|
|
4374
|
-
try {
|
|
4375
|
-
stream.unsubscribe();
|
|
4376
|
-
} catch {}
|
|
4377
|
-
this.activeStreams.delete(viewName);
|
|
4378
|
-
}
|
|
4379
|
-
this.streamScopes.delete(viewName);
|
|
4380
|
-
const store = this.stores.get(viewName);
|
|
4381
|
-
if (store)
|
|
4382
|
-
store.clear();
|
|
4383
|
-
}
|
|
4384
|
-
}
|
|
4385
|
-
setViewData(viewName, data) {
|
|
4386
|
-
const store = this.stores.get(viewName);
|
|
4387
|
-
if (!store)
|
|
4388
|
-
return;
|
|
4389
|
-
if (Array.isArray(data)) {
|
|
4390
|
-
store.setAll(data);
|
|
4391
|
-
} else if (data && typeof data === "object" && "_id" in data) {
|
|
4392
|
-
store.setAll([data]);
|
|
4393
|
-
} else {
|
|
4394
|
-
store.setAll([]);
|
|
4395
|
-
}
|
|
4420
|
+
};
|
|
4396
4421
|
}
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
if (!handler)
|
|
4402
|
-
continue;
|
|
4403
|
-
const store = this.stores.get(view3.name);
|
|
4404
|
-
if (!store)
|
|
4422
|
+
invalidateScope(scope, eventWire) {
|
|
4423
|
+
const prefix = `${scope}:`;
|
|
4424
|
+
for (const [key, entry] of this.entries) {
|
|
4425
|
+
if (!key.startsWith(prefix))
|
|
4405
4426
|
continue;
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
remove: async (id3) => {
|
|
4414
|
-
store.remove(String(id3));
|
|
4415
|
-
},
|
|
4416
|
-
find: async (options) => {
|
|
4417
|
-
return store.find(options);
|
|
4418
|
-
},
|
|
4419
|
-
findOne: async (where) => {
|
|
4420
|
-
return store.findOne(where);
|
|
4421
|
-
},
|
|
4422
|
-
$auth: {}
|
|
4423
|
-
};
|
|
4424
|
-
await handler(ctx, event3);
|
|
4427
|
+
if (entry.pendingUnsub)
|
|
4428
|
+
clearTimeout(entry.pendingUnsub);
|
|
4429
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
4430
|
+
this.entries.delete(key);
|
|
4431
|
+
entry.result = undefined;
|
|
4432
|
+
entry.hasResult = false;
|
|
4433
|
+
this.notify(entry);
|
|
4425
4434
|
}
|
|
4426
4435
|
}
|
|
4427
|
-
clear() {
|
|
4428
|
-
for (const
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
this.streamScopes.clear();
|
|
4433
|
-
for (const timeout of this.pendingUnsubscribes.values()) {
|
|
4434
|
-
clearTimeout(timeout);
|
|
4436
|
+
clear(eventWire) {
|
|
4437
|
+
for (const entry of this.entries.values()) {
|
|
4438
|
+
if (entry.pendingUnsub)
|
|
4439
|
+
clearTimeout(entry.pendingUnsub);
|
|
4440
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
4435
4441
|
}
|
|
4436
|
-
this.
|
|
4437
|
-
for (const store of this.stores.values()) {
|
|
4438
|
-
store.clear();
|
|
4439
|
-
}
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
|
|
4443
|
-
class StreamingStore {
|
|
4444
|
-
data = new Map;
|
|
4445
|
-
listeners = new Set;
|
|
4446
|
-
initialized = false;
|
|
4447
|
-
hasData() {
|
|
4448
|
-
return this.initialized;
|
|
4449
|
-
}
|
|
4450
|
-
setAll(items) {
|
|
4451
|
-
this.initialized = true;
|
|
4452
|
-
this.data.clear();
|
|
4453
|
-
for (const item of items) {
|
|
4454
|
-
this.data.set(item._id, item);
|
|
4455
|
-
}
|
|
4456
|
-
this.notifyListeners(null);
|
|
4457
|
-
}
|
|
4458
|
-
set(id3, item) {
|
|
4459
|
-
this.data.set(id3, item);
|
|
4460
|
-
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
4461
|
-
}
|
|
4462
|
-
modify(id3, updates) {
|
|
4463
|
-
const existing = this.data.get(id3);
|
|
4464
|
-
if (existing) {
|
|
4465
|
-
const updated = { ...existing, ...updates };
|
|
4466
|
-
this.data.set(id3, updated);
|
|
4467
|
-
this.notifyListeners([{ type: "set", id: id3, item: updated }]);
|
|
4468
|
-
}
|
|
4469
|
-
}
|
|
4470
|
-
remove(id3) {
|
|
4471
|
-
if (this.data.delete(id3)) {
|
|
4472
|
-
this.notifyListeners([{ type: "delete", id: id3, item: null }]);
|
|
4473
|
-
}
|
|
4474
|
-
}
|
|
4475
|
-
clear() {
|
|
4476
|
-
this.initialized = false;
|
|
4477
|
-
this.data.clear();
|
|
4478
|
-
this.notifyListeners(null);
|
|
4479
|
-
}
|
|
4480
|
-
find(options = {}) {
|
|
4481
|
-
let results = Array.from(this.data.values());
|
|
4482
|
-
if (options.where) {
|
|
4483
|
-
results = results.filter((item) => checkItemMatchesWhere(item, options.where));
|
|
4484
|
-
}
|
|
4485
|
-
return applyOrderByAndLimit(results, options);
|
|
4486
|
-
}
|
|
4487
|
-
findOne(where) {
|
|
4488
|
-
const results = this.find({ where });
|
|
4489
|
-
return results[0];
|
|
4442
|
+
this.entries.clear();
|
|
4490
4443
|
}
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
for (const listener4 of this.listeners) {
|
|
4499
|
-
listener4(events);
|
|
4444
|
+
notify(entry) {
|
|
4445
|
+
for (const listener4 of entry.listeners) {
|
|
4446
|
+
try {
|
|
4447
|
+
listener4();
|
|
4448
|
+
} catch (err) {
|
|
4449
|
+
console.error(`[Arc] Query cache listener error:`, err);
|
|
4450
|
+
}
|
|
4500
4451
|
}
|
|
4501
4452
|
}
|
|
4502
4453
|
}
|
|
4503
4454
|
|
|
4504
4455
|
class StreamingEventPublisher {
|
|
4505
|
-
cache;
|
|
4506
4456
|
eventWire;
|
|
4507
4457
|
views = [];
|
|
4508
4458
|
subscribers = new Map;
|
|
4509
|
-
constructor(
|
|
4510
|
-
this.cache = cache;
|
|
4459
|
+
constructor(eventWire) {
|
|
4511
4460
|
this.eventWire = eventWire;
|
|
4512
4461
|
}
|
|
4513
4462
|
registerViews(views) {
|
|
4514
4463
|
this.views = views;
|
|
4515
|
-
this.cache.registerViews(views);
|
|
4516
4464
|
}
|
|
4517
4465
|
async publish(event3) {
|
|
4518
|
-
await this.cache.applyEvent(event3);
|
|
4519
4466
|
await this.notifySubscribers(event3);
|
|
4520
4467
|
this.eventWire.syncEvents([
|
|
4521
4468
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-adapter-db-postgres",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"postgres": "^3.4.4"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@arcote.tech/arc": "^0.7.
|
|
26
|
+
"@arcote.tech/arc": "^0.7.16"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/pg": "^8.11.0",
|