@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.
Files changed (2) hide show
  1. package/dist/index.js +233 -290
  2. 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
- viewSubscriptions = new Map;
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.sendAllViewSubscriptions();
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
- subscribeView(element, scope, callbacks) {
1622
- const key = `${scope}:${element}`;
1623
- this.viewSubscriptions.set(key, callbacks);
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-view",
1627
- element,
1667
+ type: "subscribe-query",
1668
+ subscriptionId,
1669
+ descriptor,
1628
1670
  scope
1629
1671
  }));
1630
1672
  }
1673
+ return subscriptionId;
1631
1674
  }
1632
- unsubscribeView(element, scope) {
1633
- this.viewSubscriptions.delete(`${scope}:${element}`);
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-view",
1637
- element,
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 "view-snapshot": {
1674
- const sub = this.viewSubscriptions.get(`${message.scope}:${message.element}`);
1715
+ case "query-snapshot": {
1716
+ const sub = this.querySubscriptions.get(message.subscriptionId);
1675
1717
  if (sub) {
1676
- sub.onSnapshot(message.items ?? []);
1718
+ sub.callbacks.onSnapshot(message.result ?? null);
1677
1719
  }
1678
1720
  break;
1679
1721
  }
1680
- case "view-changes": {
1681
- const sub = this.viewSubscriptions.get(`${message.scope}:${message.element}`);
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
- sendAllViewSubscriptions() {
1753
+ sendAllQuerySubscriptions() {
1712
1754
  if (!this.ws || this.state !== "connected")
1713
1755
  return;
1714
- for (const key of this.viewSubscriptions.keys()) {
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-view",
1720
- element,
1721
- scope
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
- const viewStoreNames = new Set(viewChanges.map((c) => c.store));
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, _options) {
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, options) {
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, options) {
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, oldRow, newRow } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache, { captureRows: capture });
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, options) {
4026
- return this.source.commitChanges(changes, options);
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
- for (const query of this.trackedQueries.values()) {
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
- query.result = currentResult;
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
- var DEFAULT_SCOPE = "default";
4326
-
4327
- class StreamingQueryCache {
4328
- stores = new Map;
4329
- views = [];
4330
- activeStreams = new Map;
4331
- pendingUnsubscribes = new Map;
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
- registerStream(key, createStream) {
4351
- const pending = this.pendingUnsubscribes.get(key);
4352
- if (pending) {
4353
- clearTimeout(pending);
4354
- this.pendingUnsubscribes.delete(key);
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
- const existing = this.activeStreams.get(key);
4357
- if (existing) {
4358
- existing.refCount++;
4359
- return {
4360
- unsubscribe: () => this.unregisterStream(key),
4361
- wasReused: true
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 streamConn = createStream();
4365
- this.activeStreams.set(key, {
4366
- unsubscribe: streamConn.unsubscribe,
4367
- refCount: 1
4368
- });
4436
+ const subscribed = entry;
4437
+ subscribed.listeners.add(onChange);
4438
+ let active = true;
4369
4439
  return {
4370
- unsubscribe: () => this.unregisterStream(key),
4371
- wasReused: false
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
- unregisterStream(key) {
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, timeout] of this.pendingUnsubscribes) {
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
- try {
4418
- stream.unsubscribe();
4419
- } catch {}
4420
- this.activeStreams.delete(key);
4421
- }
4422
- for (const [key, store] of this.stores) {
4423
- if (!key.startsWith(prefix))
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
- class StreamingStore {
4476
- data = new Map;
4477
- listeners = new Set;
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.notifyListeners(null);
4482
+ this.entries.clear();
4489
4483
  }
4490
- applyChanges(events) {
4491
- if (events.length === 0)
4492
- return;
4493
- for (const event3 of events) {
4494
- if (event3.type === "set" && event3.item) {
4495
- this.data.set(event3.id, event3.item);
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(cache, eventWire) {
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.15",
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.15"
23
+ "@arcote.tech/arc": "^0.7.17"
24
24
  },
25
25
  "devDependencies": {
26
26
  "typescript": "^5.0.0"