@arcote.tech/arc-adapter-db-sqlite 0.7.15 → 0.7.17
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 +233 -290
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1473,6 +1473,42 @@ class AuthAdapter {
|
|
|
1473
1473
|
this.scopes.clear();
|
|
1474
1474
|
}
|
|
1475
1475
|
}
|
|
1476
|
+
var DEFAULT_TIMEOUT_MS = 8000;
|
|
1477
|
+
var provider = null;
|
|
1478
|
+
var latestSync = null;
|
|
1479
|
+
var syncSeq = 0;
|
|
1480
|
+
function triggerModuleSync(scope, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
1481
|
+
if (!provider) {
|
|
1482
|
+
latestSync = Promise.resolve();
|
|
1483
|
+
return latestSync;
|
|
1484
|
+
}
|
|
1485
|
+
const seq = ++syncSeq;
|
|
1486
|
+
const run = (async () => {
|
|
1487
|
+
try {
|
|
1488
|
+
await provider(scope);
|
|
1489
|
+
} catch (err) {
|
|
1490
|
+
console.warn("[arc] module sync failed during setToken:", err);
|
|
1491
|
+
}
|
|
1492
|
+
})();
|
|
1493
|
+
const guarded = new Promise((resolve) => {
|
|
1494
|
+
let settled = false;
|
|
1495
|
+
const done = () => {
|
|
1496
|
+
if (settled)
|
|
1497
|
+
return;
|
|
1498
|
+
settled = true;
|
|
1499
|
+
resolve();
|
|
1500
|
+
};
|
|
1501
|
+
const timer = setTimeout(() => {
|
|
1502
|
+
console.warn(`[arc] module sync did not complete within ${timeoutMs}ms; proceeding anyway.`);
|
|
1503
|
+
done();
|
|
1504
|
+
}, timeoutMs);
|
|
1505
|
+
timer?.unref?.();
|
|
1506
|
+
run.then(done, done).finally(() => clearTimeout(timer));
|
|
1507
|
+
});
|
|
1508
|
+
if (seq === syncSeq)
|
|
1509
|
+
latestSync = guarded;
|
|
1510
|
+
return guarded;
|
|
1511
|
+
}
|
|
1476
1512
|
var eventWireInstanceCounter = 0;
|
|
1477
1513
|
|
|
1478
1514
|
class EventWire {
|
|
@@ -1487,7 +1523,8 @@ class EventWire {
|
|
|
1487
1523
|
onSyncedCallback;
|
|
1488
1524
|
reconnectTimeout;
|
|
1489
1525
|
syncRequested = false;
|
|
1490
|
-
|
|
1526
|
+
querySubscriptions = new Map;
|
|
1527
|
+
querySubCounter = 0;
|
|
1491
1528
|
enableEventSync;
|
|
1492
1529
|
constructor(baseUrl, options) {
|
|
1493
1530
|
this.baseUrl = baseUrl;
|
|
@@ -1542,7 +1579,7 @@ class EventWire {
|
|
|
1542
1579
|
this.requestSync();
|
|
1543
1580
|
}
|
|
1544
1581
|
this.flushPendingEvents();
|
|
1545
|
-
this.
|
|
1582
|
+
this.sendAllQuerySubscriptions();
|
|
1546
1583
|
} else {
|
|
1547
1584
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
1548
1585
|
}
|
|
@@ -1618,24 +1655,29 @@ class EventWire {
|
|
|
1618
1655
|
onSynced(callback) {
|
|
1619
1656
|
this.onSyncedCallback = callback;
|
|
1620
1657
|
}
|
|
1621
|
-
|
|
1622
|
-
const
|
|
1623
|
-
this.
|
|
1658
|
+
subscribeQuery(descriptor, scope, callbacks) {
|
|
1659
|
+
const subscriptionId = `qs_${this.instanceId}_${++this.querySubCounter}`;
|
|
1660
|
+
this.querySubscriptions.set(subscriptionId, {
|
|
1661
|
+
descriptor,
|
|
1662
|
+
scope,
|
|
1663
|
+
callbacks
|
|
1664
|
+
});
|
|
1624
1665
|
if (this.state === "connected" && this.ws) {
|
|
1625
1666
|
this.ws.send(JSON.stringify({
|
|
1626
|
-
type: "subscribe-
|
|
1627
|
-
|
|
1667
|
+
type: "subscribe-query",
|
|
1668
|
+
subscriptionId,
|
|
1669
|
+
descriptor,
|
|
1628
1670
|
scope
|
|
1629
1671
|
}));
|
|
1630
1672
|
}
|
|
1673
|
+
return subscriptionId;
|
|
1631
1674
|
}
|
|
1632
|
-
|
|
1633
|
-
this.
|
|
1675
|
+
unsubscribeQuery(subscriptionId) {
|
|
1676
|
+
this.querySubscriptions.delete(subscriptionId);
|
|
1634
1677
|
if (this.state === "connected" && this.ws) {
|
|
1635
1678
|
this.ws.send(JSON.stringify({
|
|
1636
|
-
type: "unsubscribe-
|
|
1637
|
-
|
|
1638
|
-
scope
|
|
1679
|
+
type: "unsubscribe-query",
|
|
1680
|
+
subscriptionId
|
|
1639
1681
|
}));
|
|
1640
1682
|
}
|
|
1641
1683
|
}
|
|
@@ -1670,17 +1712,17 @@ class EventWire {
|
|
|
1670
1712
|
this.lastHostEventId = message.lastHostEventId;
|
|
1671
1713
|
}
|
|
1672
1714
|
break;
|
|
1673
|
-
case "
|
|
1674
|
-
const sub = this.
|
|
1715
|
+
case "query-snapshot": {
|
|
1716
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
1675
1717
|
if (sub) {
|
|
1676
|
-
sub.onSnapshot(message.
|
|
1718
|
+
sub.callbacks.onSnapshot(message.result ?? null);
|
|
1677
1719
|
}
|
|
1678
1720
|
break;
|
|
1679
1721
|
}
|
|
1680
|
-
case "
|
|
1681
|
-
const sub = this.
|
|
1722
|
+
case "query-changes": {
|
|
1723
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
1682
1724
|
if (sub && Array.isArray(message.changes)) {
|
|
1683
|
-
sub.onChanges(message.changes);
|
|
1725
|
+
sub.callbacks.onChanges(message.changes);
|
|
1684
1726
|
}
|
|
1685
1727
|
break;
|
|
1686
1728
|
}
|
|
@@ -1708,17 +1750,15 @@ class EventWire {
|
|
|
1708
1750
|
this.pendingEvents = [];
|
|
1709
1751
|
}
|
|
1710
1752
|
}
|
|
1711
|
-
|
|
1753
|
+
sendAllQuerySubscriptions() {
|
|
1712
1754
|
if (!this.ws || this.state !== "connected")
|
|
1713
1755
|
return;
|
|
1714
|
-
for (const
|
|
1715
|
-
const sepIdx = key.indexOf(":");
|
|
1716
|
-
const scope = key.slice(0, sepIdx);
|
|
1717
|
-
const element = key.slice(sepIdx + 1);
|
|
1756
|
+
for (const [subscriptionId, sub] of this.querySubscriptions) {
|
|
1718
1757
|
this.ws.send(JSON.stringify({
|
|
1719
|
-
type: "subscribe-
|
|
1720
|
-
|
|
1721
|
-
|
|
1758
|
+
type: "subscribe-query",
|
|
1759
|
+
subscriptionId,
|
|
1760
|
+
descriptor: sub.descriptor,
|
|
1761
|
+
scope: sub.scope
|
|
1722
1762
|
}));
|
|
1723
1763
|
}
|
|
1724
1764
|
}
|
|
@@ -1742,19 +1782,12 @@ class LocalEventPublisher {
|
|
|
1742
1782
|
views = [];
|
|
1743
1783
|
syncCallback;
|
|
1744
1784
|
subscribers = new Map;
|
|
1745
|
-
viewChangesCallbacks = new Set;
|
|
1746
1785
|
constructor(dataStorage) {
|
|
1747
1786
|
this.dataStorage = dataStorage;
|
|
1748
1787
|
}
|
|
1749
1788
|
onPublish(callback) {
|
|
1750
1789
|
this.syncCallback = callback;
|
|
1751
1790
|
}
|
|
1752
|
-
onViewChanges(callback) {
|
|
1753
|
-
this.viewChangesCallbacks.add(callback);
|
|
1754
|
-
return () => {
|
|
1755
|
-
this.viewChangesCallbacks.delete(callback);
|
|
1756
|
-
};
|
|
1757
|
-
}
|
|
1758
1791
|
registerViews(views) {
|
|
1759
1792
|
this.views = views;
|
|
1760
1793
|
}
|
|
@@ -1822,19 +1855,7 @@ class LocalEventPublisher {
|
|
|
1822
1855
|
});
|
|
1823
1856
|
const viewChanges = await this.collectViewChanges(event);
|
|
1824
1857
|
allChanges.push(...viewChanges);
|
|
1825
|
-
|
|
1826
|
-
const committed = await this.dataStorage.commitChanges(allChanges, {
|
|
1827
|
-
captureRowsFor: viewStoreNames
|
|
1828
|
-
});
|
|
1829
|
-
if (committed.length > 0 && this.viewChangesCallbacks.size > 0) {
|
|
1830
|
-
for (const callback of this.viewChangesCallbacks) {
|
|
1831
|
-
try {
|
|
1832
|
-
callback(committed);
|
|
1833
|
-
} catch (error) {
|
|
1834
|
-
console.error(`[EventPublisher] onViewChanges callback error:`, error);
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1858
|
+
await this.dataStorage.commitChanges(allChanges);
|
|
1838
1859
|
await this.notifySubscribers(event);
|
|
1839
1860
|
if (this.syncCallback) {
|
|
1840
1861
|
this.syncCallback(event);
|
|
@@ -2474,9 +2495,8 @@ class ArcObject extends ArcAbstract {
|
|
|
2474
2495
|
}
|
|
2475
2496
|
}
|
|
2476
2497
|
class DataStorage {
|
|
2477
|
-
async commitChanges(changes
|
|
2498
|
+
async commitChanges(changes) {
|
|
2478
2499
|
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
2479
|
-
return [];
|
|
2480
2500
|
}
|
|
2481
2501
|
}
|
|
2482
2502
|
|
|
@@ -3567,6 +3587,48 @@ function deepMerge(target, source) {
|
|
|
3567
3587
|
function isPlainObject(item) {
|
|
3568
3588
|
return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
|
|
3569
3589
|
}
|
|
3590
|
+
function murmurHash(key, seed = 0) {
|
|
3591
|
+
let remainder, bytes, h1, h1b, c1, c2, k1, i;
|
|
3592
|
+
remainder = key.length & 3;
|
|
3593
|
+
bytes = key.length - remainder;
|
|
3594
|
+
h1 = seed;
|
|
3595
|
+
c1 = 3432918353;
|
|
3596
|
+
c2 = 461845907;
|
|
3597
|
+
i = 0;
|
|
3598
|
+
while (i < bytes) {
|
|
3599
|
+
k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
|
|
3600
|
+
++i;
|
|
3601
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
3602
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
3603
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
3604
|
+
h1 ^= k1;
|
|
3605
|
+
h1 = h1 << 13 | h1 >>> 19;
|
|
3606
|
+
h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
|
|
3607
|
+
h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
|
|
3608
|
+
}
|
|
3609
|
+
k1 = 0;
|
|
3610
|
+
if (remainder >= 3) {
|
|
3611
|
+
k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
|
|
3612
|
+
}
|
|
3613
|
+
if (remainder >= 2) {
|
|
3614
|
+
k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
|
|
3615
|
+
}
|
|
3616
|
+
if (remainder >= 1) {
|
|
3617
|
+
k1 ^= key.charCodeAt(i) & 255;
|
|
3618
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
3619
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
3620
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
3621
|
+
h1 ^= k1;
|
|
3622
|
+
}
|
|
3623
|
+
h1 ^= key.length;
|
|
3624
|
+
h1 ^= h1 >>> 16;
|
|
3625
|
+
h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
|
|
3626
|
+
h1 ^= h1 >>> 13;
|
|
3627
|
+
h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
|
|
3628
|
+
h1 ^= h1 >>> 16;
|
|
3629
|
+
return h1 >>> 0;
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3570
3632
|
class ForkedStoreState extends StoreState {
|
|
3571
3633
|
master;
|
|
3572
3634
|
changedItems = new Map;
|
|
@@ -3722,12 +3784,8 @@ class MasterStoreState extends StoreState {
|
|
|
3722
3784
|
}
|
|
3723
3785
|
return transaction.find(this.storeName, { where: { _id: id2 } }).then((results) => results[0]);
|
|
3724
3786
|
}
|
|
3725
|
-
async applyChangeAndReturnEvent(transaction, change, transactionCache
|
|
3787
|
+
async applyChangeAndReturnEvent(transaction, change, transactionCache) {
|
|
3726
3788
|
if (change.type === "set") {
|
|
3727
|
-
let existing;
|
|
3728
|
-
if (options?.captureRows) {
|
|
3729
|
-
existing = await this.readExisting(transaction, change.data._id, transactionCache);
|
|
3730
|
-
}
|
|
3731
3789
|
await transaction.set(this.storeName, change.data);
|
|
3732
3790
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
3733
3791
|
if (transactionCache) {
|
|
@@ -3740,16 +3798,10 @@ class MasterStoreState extends StoreState {
|
|
|
3740
3798
|
type: "set",
|
|
3741
3799
|
item: change.data,
|
|
3742
3800
|
id: change.data._id
|
|
3743
|
-
}
|
|
3744
|
-
oldRow: existing ?? null,
|
|
3745
|
-
newRow: change.data
|
|
3801
|
+
}
|
|
3746
3802
|
};
|
|
3747
3803
|
}
|
|
3748
3804
|
if (change.type === "delete") {
|
|
3749
|
-
let existing;
|
|
3750
|
-
if (options?.captureRows) {
|
|
3751
|
-
existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
3752
|
-
}
|
|
3753
3805
|
await transaction.remove(this.storeName, change.id);
|
|
3754
3806
|
if (transactionCache) {
|
|
3755
3807
|
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
@@ -3761,9 +3813,7 @@ class MasterStoreState extends StoreState {
|
|
|
3761
3813
|
type: "delete",
|
|
3762
3814
|
item: null,
|
|
3763
3815
|
id: change.id
|
|
3764
|
-
}
|
|
3765
|
-
oldRow: existing ?? null,
|
|
3766
|
-
newRow: null
|
|
3816
|
+
}
|
|
3767
3817
|
};
|
|
3768
3818
|
}
|
|
3769
3819
|
if (change.type === "modify") {
|
|
@@ -3781,9 +3831,7 @@ class MasterStoreState extends StoreState {
|
|
|
3781
3831
|
type: "set",
|
|
3782
3832
|
item,
|
|
3783
3833
|
id: change.id
|
|
3784
|
-
}
|
|
3785
|
-
oldRow: existing ?? null,
|
|
3786
|
-
newRow: updated
|
|
3834
|
+
}
|
|
3787
3835
|
};
|
|
3788
3836
|
}
|
|
3789
3837
|
if (change.type === "mutate") {
|
|
@@ -3801,9 +3849,7 @@ class MasterStoreState extends StoreState {
|
|
|
3801
3849
|
type: "set",
|
|
3802
3850
|
item,
|
|
3803
3851
|
id: change.id
|
|
3804
|
-
}
|
|
3805
|
-
oldRow: existing ?? null,
|
|
3806
|
-
newRow: updated
|
|
3852
|
+
}
|
|
3807
3853
|
};
|
|
3808
3854
|
}
|
|
3809
3855
|
throw new Error("Unknown change type");
|
|
@@ -3881,22 +3927,17 @@ class MasterDataStorage extends DataStorage {
|
|
|
3881
3927
|
applySerializedChanges(changes) {
|
|
3882
3928
|
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
3883
3929
|
}
|
|
3884
|
-
async commitChanges(changes
|
|
3930
|
+
async commitChanges(changes) {
|
|
3885
3931
|
const transaction = await this.getReadWriteTransaction();
|
|
3886
3932
|
const transactionCache = new Map;
|
|
3887
3933
|
const eventsByStore = new Map;
|
|
3888
|
-
const committed = [];
|
|
3889
3934
|
for (const { store, changes: storeChanges } of changes) {
|
|
3890
3935
|
const storeState = this.getStore(store);
|
|
3891
3936
|
const storeEvents = [];
|
|
3892
|
-
const capture = options?.captureRowsFor?.has(store) ?? false;
|
|
3893
3937
|
for (const change of storeChanges) {
|
|
3894
|
-
const { event: event3
|
|
3938
|
+
const { event: event3 } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
3895
3939
|
if (event3)
|
|
3896
3940
|
storeEvents.push(event3);
|
|
3897
|
-
if (capture) {
|
|
3898
|
-
committed.push({ store, id: event3.id, oldRow, newRow });
|
|
3899
|
-
}
|
|
3900
3941
|
}
|
|
3901
3942
|
if (storeEvents.length > 0) {
|
|
3902
3943
|
eventsByStore.set(store, storeEvents);
|
|
@@ -3907,7 +3948,6 @@ class MasterDataStorage extends DataStorage {
|
|
|
3907
3948
|
const storeState = this.getStore(store);
|
|
3908
3949
|
storeState.notifyListenersPublic(events);
|
|
3909
3950
|
}
|
|
3910
|
-
return committed;
|
|
3911
3951
|
}
|
|
3912
3952
|
fork() {
|
|
3913
3953
|
return new ForkedDataStorage(this);
|
|
@@ -4022,8 +4062,8 @@ class ObservableDataStorage {
|
|
|
4022
4062
|
getReadWriteTransaction() {
|
|
4023
4063
|
return this.source.getReadWriteTransaction();
|
|
4024
4064
|
}
|
|
4025
|
-
commitChanges(changes
|
|
4026
|
-
return this.source.commitChanges(changes
|
|
4065
|
+
commitChanges(changes) {
|
|
4066
|
+
return this.source.commitChanges(changes);
|
|
4027
4067
|
}
|
|
4028
4068
|
trackQuery(storeName, options, result, listener4) {
|
|
4029
4069
|
const key = this.getQueryKey(storeName, options);
|
|
@@ -4036,7 +4076,8 @@ class ObservableDataStorage {
|
|
|
4036
4076
|
}
|
|
4037
4077
|
handleStoreChange(storeName, events) {
|
|
4038
4078
|
let hasChanges = false;
|
|
4039
|
-
|
|
4079
|
+
const staleKeys = [];
|
|
4080
|
+
for (const [key, query] of this.trackedQueries) {
|
|
4040
4081
|
if (query.storeName !== storeName)
|
|
4041
4082
|
continue;
|
|
4042
4083
|
let currentResult = query.result;
|
|
@@ -4048,10 +4089,20 @@ class ObservableDataStorage {
|
|
|
4048
4089
|
queryChanged = true;
|
|
4049
4090
|
}
|
|
4050
4091
|
}
|
|
4051
|
-
if (queryChanged)
|
|
4052
|
-
|
|
4092
|
+
if (!queryChanged)
|
|
4093
|
+
continue;
|
|
4094
|
+
if (query.options.limit !== undefined && query.result.length === query.options.limit && currentResult.length < query.options.limit) {
|
|
4095
|
+
staleKeys.push(key);
|
|
4053
4096
|
hasChanges = true;
|
|
4097
|
+
continue;
|
|
4054
4098
|
}
|
|
4099
|
+
query.result = currentResult;
|
|
4100
|
+
hasChanges = true;
|
|
4101
|
+
}
|
|
4102
|
+
for (const key of staleKeys) {
|
|
4103
|
+
const query = this.trackedQueries.get(key);
|
|
4104
|
+
this.source.getStore(query.storeName).unsubscribe(query.listener);
|
|
4105
|
+
this.trackedQueries.delete(key);
|
|
4055
4106
|
}
|
|
4056
4107
|
if (hasChanges) {
|
|
4057
4108
|
this.onChange();
|
|
@@ -4225,6 +4276,7 @@ class ScopedModel {
|
|
|
4225
4276
|
for (const listener4 of this.tokenListeners) {
|
|
4226
4277
|
listener4();
|
|
4227
4278
|
}
|
|
4279
|
+
return triggerModuleSync(this.scopeName);
|
|
4228
4280
|
}
|
|
4229
4281
|
getToken() {
|
|
4230
4282
|
return this.authAdapter.getToken();
|
|
@@ -4322,244 +4374,135 @@ class Model {
|
|
|
4322
4374
|
return s;
|
|
4323
4375
|
}
|
|
4324
4376
|
}
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
4333
|
-
storeKey(viewName, scope) {
|
|
4334
|
-
return `${scope ?? DEFAULT_SCOPE}:${viewName}`;
|
|
4335
|
-
}
|
|
4336
|
-
registerViews(views) {
|
|
4337
|
-
this.views = views;
|
|
4338
|
-
}
|
|
4339
|
-
getStore(viewName, scope) {
|
|
4340
|
-
const key = this.storeKey(viewName, scope);
|
|
4341
|
-
if (!this.stores.has(key)) {
|
|
4342
|
-
this.stores.set(key, new StreamingStore);
|
|
4377
|
+
function applyQueryChanges(result, changes) {
|
|
4378
|
+
const next = [...result];
|
|
4379
|
+
for (const change of changes) {
|
|
4380
|
+
if (change.type === "delete") {
|
|
4381
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
4382
|
+
if (idx !== -1)
|
|
4383
|
+
next.splice(idx, 1);
|
|
4343
4384
|
}
|
|
4344
|
-
return this.stores.get(key);
|
|
4345
|
-
}
|
|
4346
|
-
hasData(viewName, scope) {
|
|
4347
|
-
const store = this.stores.get(this.storeKey(viewName, scope));
|
|
4348
|
-
return store ? store.hasData() : false;
|
|
4349
4385
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4386
|
+
for (const change of changes) {
|
|
4387
|
+
if (change.type === "set") {
|
|
4388
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
4389
|
+
if (idx !== -1)
|
|
4390
|
+
next.splice(idx, 1);
|
|
4391
|
+
next.splice(change.index, 0, change.item);
|
|
4355
4392
|
}
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4393
|
+
}
|
|
4394
|
+
return next;
|
|
4395
|
+
}
|
|
4396
|
+
class StreamingQueryCache {
|
|
4397
|
+
entries = new Map;
|
|
4398
|
+
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
4399
|
+
entryKey(descriptor, scope) {
|
|
4400
|
+
return `${scope ?? "default"}:${murmurHash(JSON.stringify(descriptor))}`;
|
|
4401
|
+
}
|
|
4402
|
+
subscribe(descriptor, scope, eventWire, onChange) {
|
|
4403
|
+
const key = this.entryKey(descriptor, scope);
|
|
4404
|
+
let entry = this.entries.get(key);
|
|
4405
|
+
if (entry) {
|
|
4406
|
+
if (entry.pendingUnsub) {
|
|
4407
|
+
clearTimeout(entry.pendingUnsub);
|
|
4408
|
+
entry.pendingUnsub = undefined;
|
|
4409
|
+
}
|
|
4410
|
+
entry.refCount++;
|
|
4411
|
+
} else {
|
|
4412
|
+
const newEntry = {
|
|
4413
|
+
result: undefined,
|
|
4414
|
+
hasResult: false,
|
|
4415
|
+
listeners: new Set,
|
|
4416
|
+
refCount: 1,
|
|
4417
|
+
subscriptionId: ""
|
|
4362
4418
|
};
|
|
4419
|
+
newEntry.subscriptionId = eventWire.subscribeQuery(descriptor, scope, {
|
|
4420
|
+
onSnapshot: (result) => {
|
|
4421
|
+
newEntry.result = result;
|
|
4422
|
+
newEntry.hasResult = true;
|
|
4423
|
+
this.notify(newEntry);
|
|
4424
|
+
},
|
|
4425
|
+
onChanges: (changes) => {
|
|
4426
|
+
if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
|
|
4427
|
+
return;
|
|
4428
|
+
}
|
|
4429
|
+
newEntry.result = applyQueryChanges(newEntry.result, changes);
|
|
4430
|
+
this.notify(newEntry);
|
|
4431
|
+
}
|
|
4432
|
+
});
|
|
4433
|
+
this.entries.set(key, newEntry);
|
|
4434
|
+
entry = newEntry;
|
|
4363
4435
|
}
|
|
4364
|
-
const
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
refCount: 1
|
|
4368
|
-
});
|
|
4436
|
+
const subscribed = entry;
|
|
4437
|
+
subscribed.listeners.add(onChange);
|
|
4438
|
+
let active = true;
|
|
4369
4439
|
return {
|
|
4370
|
-
|
|
4371
|
-
|
|
4440
|
+
read: () => ({
|
|
4441
|
+
result: subscribed.result,
|
|
4442
|
+
loading: !subscribed.hasResult
|
|
4443
|
+
}),
|
|
4444
|
+
unsubscribe: () => {
|
|
4445
|
+
if (!active)
|
|
4446
|
+
return;
|
|
4447
|
+
active = false;
|
|
4448
|
+
subscribed.listeners.delete(onChange);
|
|
4449
|
+
subscribed.refCount--;
|
|
4450
|
+
if (subscribed.refCount > 0)
|
|
4451
|
+
return;
|
|
4452
|
+
subscribed.pendingUnsub = setTimeout(() => {
|
|
4453
|
+
subscribed.pendingUnsub = undefined;
|
|
4454
|
+
if (subscribed.refCount > 0)
|
|
4455
|
+
return;
|
|
4456
|
+
eventWire.unsubscribeQuery(subscribed.subscriptionId);
|
|
4457
|
+
this.entries.delete(key);
|
|
4458
|
+
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
4459
|
+
}
|
|
4372
4460
|
};
|
|
4373
4461
|
}
|
|
4374
|
-
|
|
4375
|
-
const stream = this.activeStreams.get(key);
|
|
4376
|
-
if (!stream)
|
|
4377
|
-
return;
|
|
4378
|
-
stream.refCount--;
|
|
4379
|
-
if (stream.refCount <= 0) {
|
|
4380
|
-
const timeout = setTimeout(() => {
|
|
4381
|
-
this.pendingUnsubscribes.delete(key);
|
|
4382
|
-
const current2 = this.activeStreams.get(key);
|
|
4383
|
-
if (current2 && current2.refCount <= 0) {
|
|
4384
|
-
current2.unsubscribe();
|
|
4385
|
-
this.activeStreams.delete(key);
|
|
4386
|
-
}
|
|
4387
|
-
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
4388
|
-
this.pendingUnsubscribes.set(key, timeout);
|
|
4389
|
-
}
|
|
4390
|
-
}
|
|
4391
|
-
subscribeView(viewName, eventWire, scope) {
|
|
4392
|
-
const key = this.storeKey(viewName, scope);
|
|
4393
|
-
const { unsubscribe } = this.registerStream(key, () => {
|
|
4394
|
-
const store = this.stores.get(key) ?? new StreamingStore;
|
|
4395
|
-
this.stores.set(key, store);
|
|
4396
|
-
eventWire.subscribeView(viewName, scope ?? DEFAULT_SCOPE, {
|
|
4397
|
-
onSnapshot: (items) => store.setAll(items),
|
|
4398
|
-
onChanges: (changes) => store.applyChanges(changes)
|
|
4399
|
-
});
|
|
4400
|
-
return {
|
|
4401
|
-
unsubscribe: () => eventWire.unsubscribeView(viewName, scope ?? DEFAULT_SCOPE)
|
|
4402
|
-
};
|
|
4403
|
-
});
|
|
4404
|
-
return unsubscribe;
|
|
4405
|
-
}
|
|
4406
|
-
invalidateScope(scope) {
|
|
4462
|
+
invalidateScope(scope, eventWire) {
|
|
4407
4463
|
const prefix = `${scope}:`;
|
|
4408
|
-
for (const [key,
|
|
4409
|
-
if (!key.startsWith(prefix))
|
|
4410
|
-
continue;
|
|
4411
|
-
clearTimeout(timeout);
|
|
4412
|
-
this.pendingUnsubscribes.delete(key);
|
|
4413
|
-
}
|
|
4414
|
-
for (const [key, stream] of this.activeStreams) {
|
|
4464
|
+
for (const [key, entry] of this.entries) {
|
|
4415
4465
|
if (!key.startsWith(prefix))
|
|
4416
4466
|
continue;
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
this.
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
continue;
|
|
4425
|
-
store.clear();
|
|
4426
|
-
}
|
|
4427
|
-
}
|
|
4428
|
-
async applyEvent(event3) {
|
|
4429
|
-
for (const view3 of this.views) {
|
|
4430
|
-
const handlers = view3.getHandlers();
|
|
4431
|
-
const handler = handlers[event3.type];
|
|
4432
|
-
if (!handler)
|
|
4433
|
-
continue;
|
|
4434
|
-
const suffix = `:${view3.name}`;
|
|
4435
|
-
for (const [key, store] of this.stores) {
|
|
4436
|
-
if (!key.endsWith(suffix))
|
|
4437
|
-
continue;
|
|
4438
|
-
const ctx = {
|
|
4439
|
-
set: async (id3, data) => {
|
|
4440
|
-
store.set(String(id3), { _id: String(id3), ...data });
|
|
4441
|
-
},
|
|
4442
|
-
modify: async (id3, data) => {
|
|
4443
|
-
store.modify(String(id3), data);
|
|
4444
|
-
},
|
|
4445
|
-
remove: async (id3) => {
|
|
4446
|
-
store.remove(String(id3));
|
|
4447
|
-
},
|
|
4448
|
-
find: async (options) => {
|
|
4449
|
-
return store.find(options);
|
|
4450
|
-
},
|
|
4451
|
-
findOne: async (where) => {
|
|
4452
|
-
return store.findOne(where);
|
|
4453
|
-
},
|
|
4454
|
-
$auth: {}
|
|
4455
|
-
};
|
|
4456
|
-
await handler(ctx, event3);
|
|
4457
|
-
}
|
|
4458
|
-
}
|
|
4459
|
-
}
|
|
4460
|
-
clear() {
|
|
4461
|
-
for (const stream of this.activeStreams.values()) {
|
|
4462
|
-
stream.unsubscribe();
|
|
4463
|
-
}
|
|
4464
|
-
this.activeStreams.clear();
|
|
4465
|
-
for (const timeout of this.pendingUnsubscribes.values()) {
|
|
4466
|
-
clearTimeout(timeout);
|
|
4467
|
-
}
|
|
4468
|
-
this.pendingUnsubscribes.clear();
|
|
4469
|
-
for (const store of this.stores.values()) {
|
|
4470
|
-
store.clear();
|
|
4467
|
+
if (entry.pendingUnsub)
|
|
4468
|
+
clearTimeout(entry.pendingUnsub);
|
|
4469
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
4470
|
+
this.entries.delete(key);
|
|
4471
|
+
entry.result = undefined;
|
|
4472
|
+
entry.hasResult = false;
|
|
4473
|
+
this.notify(entry);
|
|
4471
4474
|
}
|
|
4472
4475
|
}
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
initialized = false;
|
|
4479
|
-
hasData() {
|
|
4480
|
-
return this.initialized;
|
|
4481
|
-
}
|
|
4482
|
-
setAll(items) {
|
|
4483
|
-
this.initialized = true;
|
|
4484
|
-
this.data.clear();
|
|
4485
|
-
for (const item of items) {
|
|
4486
|
-
this.data.set(item._id, item);
|
|
4476
|
+
clear(eventWire) {
|
|
4477
|
+
for (const entry of this.entries.values()) {
|
|
4478
|
+
if (entry.pendingUnsub)
|
|
4479
|
+
clearTimeout(entry.pendingUnsub);
|
|
4480
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
4487
4481
|
}
|
|
4488
|
-
this.
|
|
4482
|
+
this.entries.clear();
|
|
4489
4483
|
}
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
} else if (event3.type === "delete") {
|
|
4497
|
-
this.data.delete(event3.id);
|
|
4484
|
+
notify(entry) {
|
|
4485
|
+
for (const listener4 of entry.listeners) {
|
|
4486
|
+
try {
|
|
4487
|
+
listener4();
|
|
4488
|
+
} catch (err) {
|
|
4489
|
+
console.error(`[Arc] Query cache listener error:`, err);
|
|
4498
4490
|
}
|
|
4499
4491
|
}
|
|
4500
|
-
this.notifyListeners(events);
|
|
4501
|
-
}
|
|
4502
|
-
set(id3, item) {
|
|
4503
|
-
this.data.set(id3, item);
|
|
4504
|
-
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
4505
|
-
}
|
|
4506
|
-
modify(id3, updates) {
|
|
4507
|
-
const existing = this.data.get(id3);
|
|
4508
|
-
if (existing) {
|
|
4509
|
-
const updated = { ...existing, ...updates };
|
|
4510
|
-
this.data.set(id3, updated);
|
|
4511
|
-
this.notifyListeners([{ type: "set", id: id3, item: updated }]);
|
|
4512
|
-
}
|
|
4513
|
-
}
|
|
4514
|
-
remove(id3) {
|
|
4515
|
-
if (this.data.delete(id3)) {
|
|
4516
|
-
this.notifyListeners([{ type: "delete", id: id3, item: null }]);
|
|
4517
|
-
}
|
|
4518
|
-
}
|
|
4519
|
-
clear() {
|
|
4520
|
-
this.initialized = false;
|
|
4521
|
-
this.data.clear();
|
|
4522
|
-
this.notifyListeners(null);
|
|
4523
|
-
}
|
|
4524
|
-
find(options = {}) {
|
|
4525
|
-
let results = Array.from(this.data.values());
|
|
4526
|
-
if (options.where) {
|
|
4527
|
-
results = results.filter((item) => checkItemMatchesWhere(item, options.where));
|
|
4528
|
-
}
|
|
4529
|
-
return applyOrderByAndLimit(results, options);
|
|
4530
|
-
}
|
|
4531
|
-
findOne(where) {
|
|
4532
|
-
const results = this.find({ where });
|
|
4533
|
-
return results[0];
|
|
4534
|
-
}
|
|
4535
|
-
subscribe(listener4) {
|
|
4536
|
-
this.listeners.add(listener4);
|
|
4537
|
-
return () => {
|
|
4538
|
-
this.listeners.delete(listener4);
|
|
4539
|
-
};
|
|
4540
|
-
}
|
|
4541
|
-
notifyListeners(events) {
|
|
4542
|
-
for (const listener4 of this.listeners) {
|
|
4543
|
-
listener4(events);
|
|
4544
|
-
}
|
|
4545
4492
|
}
|
|
4546
4493
|
}
|
|
4547
4494
|
|
|
4548
4495
|
class StreamingEventPublisher {
|
|
4549
|
-
cache;
|
|
4550
4496
|
eventWire;
|
|
4551
4497
|
views = [];
|
|
4552
4498
|
subscribers = new Map;
|
|
4553
|
-
constructor(
|
|
4554
|
-
this.cache = cache;
|
|
4499
|
+
constructor(eventWire) {
|
|
4555
4500
|
this.eventWire = eventWire;
|
|
4556
4501
|
}
|
|
4557
4502
|
registerViews(views) {
|
|
4558
4503
|
this.views = views;
|
|
4559
|
-
this.cache.registerViews(views);
|
|
4560
4504
|
}
|
|
4561
4505
|
async publish(event3) {
|
|
4562
|
-
await this.cache.applyEvent(event3);
|
|
4563
4506
|
await this.notifySubscribers(event3);
|
|
4564
4507
|
this.eventWire.syncEvents([
|
|
4565
4508
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-adapter-db-sqlite",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test": "bun test"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@arcote.tech/arc": "^0.7.
|
|
23
|
+
"@arcote.tech/arc": "^0.7.17"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"typescript": "^5.0.0"
|