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