@arcote.tech/arc 0.3.2 → 0.3.3

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 CHANGED
@@ -3536,6 +3536,7 @@ function mutationExecutor(model) {
3536
3536
  class StreamingQueryCache {
3537
3537
  stores = new Map;
3538
3538
  views = [];
3539
+ activeStreams = new Map;
3539
3540
  registerViews(views) {
3540
3541
  this.views = views;
3541
3542
  for (const view3 of views) {
@@ -3550,6 +3551,32 @@ class StreamingQueryCache {
3550
3551
  }
3551
3552
  return this.stores.get(viewName);
3552
3553
  }
3554
+ hasActiveStream(viewName) {
3555
+ return this.activeStreams.has(viewName);
3556
+ }
3557
+ registerStream(viewName, createStream) {
3558
+ const existing = this.activeStreams.get(viewName);
3559
+ if (existing) {
3560
+ existing.refCount++;
3561
+ return () => this.unregisterStream(viewName);
3562
+ }
3563
+ const streamConn = createStream();
3564
+ this.activeStreams.set(viewName, {
3565
+ unsubscribe: streamConn.unsubscribe,
3566
+ refCount: 1
3567
+ });
3568
+ return () => this.unregisterStream(viewName);
3569
+ }
3570
+ unregisterStream(viewName) {
3571
+ const stream = this.activeStreams.get(viewName);
3572
+ if (!stream)
3573
+ return;
3574
+ stream.refCount--;
3575
+ if (stream.refCount <= 0) {
3576
+ stream.unsubscribe();
3577
+ this.activeStreams.delete(viewName);
3578
+ }
3579
+ }
3553
3580
  setViewData(viewName, items) {
3554
3581
  const store = this.stores.get(viewName);
3555
3582
  if (store) {
@@ -3587,6 +3614,10 @@ class StreamingQueryCache {
3587
3614
  }
3588
3615
  }
3589
3616
  clear() {
3617
+ for (const stream of this.activeStreams.values()) {
3618
+ stream.unsubscribe();
3619
+ }
3620
+ this.activeStreams.clear();
3590
3621
  for (const store of this.stores.values()) {
3591
3622
  store.clear();
3592
3623
  }
@@ -3716,7 +3747,6 @@ function streamingLiveQuery(model, queryFn, callback, options) {
3716
3747
  const { queryWire, cache, authToken } = options;
3717
3748
  let currentResult = undefined;
3718
3749
  const unsubscribers = [];
3719
- const streamConnections = [];
3720
3750
  const queriedViews = new Set;
3721
3751
  const queryContext = new Proxy({}, {
3722
3752
  get(_target, viewName) {
@@ -3747,14 +3777,20 @@ function streamingLiveQuery(model, queryFn, callback, options) {
3747
3777
  await queryFn(queryContext);
3748
3778
  for (const viewName of queriedViews) {
3749
3779
  const store = cache.getStore(viewName);
3750
- const unsub = store.subscribe(() => {
3780
+ const cacheUnsub = store.subscribe(() => {
3751
3781
  executeQuery();
3752
3782
  });
3753
- unsubscribers.push(unsub);
3754
- const streamConn = queryWire.stream(viewName, {}, (data) => {
3755
- cache.setViewData(viewName, data);
3756
- }, authToken);
3757
- streamConnections.push(streamConn);
3783
+ unsubscribers.push(cacheUnsub);
3784
+ const streamAlreadyExists = cache.hasActiveStream(viewName);
3785
+ const streamUnsub = cache.registerStream(viewName, () => {
3786
+ return queryWire.stream(viewName, {}, (data) => {
3787
+ cache.setViewData(viewName, data);
3788
+ }, authToken);
3789
+ });
3790
+ unsubscribers.push(streamUnsub);
3791
+ if (streamAlreadyExists) {
3792
+ executeQuery();
3793
+ }
3758
3794
  }
3759
3795
  };
3760
3796
  setupStreams();
@@ -3766,9 +3802,6 @@ function streamingLiveQuery(model, queryFn, callback, options) {
3766
3802
  for (const unsub of unsubscribers) {
3767
3803
  unsub();
3768
3804
  }
3769
- for (const conn of streamConnections) {
3770
- conn.unsubscribe();
3771
- }
3772
3805
  }
3773
3806
  };
3774
3807
  }
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Uses SSE stream to get initial data and updates from server.
5
5
  * Uses StreamingQueryCache for local state and reactivity.
6
+ * Deduplicates SSE streams - multiple queries on same view share one stream.
6
7
  */
7
8
  import type { QueryWire } from "../adapters/query-wire";
8
9
  import type { ArcContextAny } from "../context/context";
@@ -18,6 +19,7 @@ export interface StreamingLiveQueryOptions {
18
19
  * Create a streaming live query
19
20
  *
20
21
  * Opens SSE stream to server, populates cache, and reacts to changes.
22
+ * Reuses existing streams for the same view (deduplication via cache).
21
23
  */
22
24
  export declare function streamingLiveQuery<C extends ArcContextAny, TResult>(model: Model<C>, queryFn: (q: QueryContext<C>) => Promise<TResult>, callback: (data: TResult) => void, options: StreamingLiveQueryOptions): LiveQueryResult<TResult>;
23
25
  //# sourceMappingURL=streaming-live-query.d.ts.map
@@ -9,6 +9,7 @@
9
9
  * - Supports reactive queries with listeners
10
10
  * - Applies view handlers for local event emission
11
11
  * - Receives updates from SSE stream
12
+ * - Deduplicates SSE streams (one stream per view)
12
13
  */
13
14
  import type { ArcEventAny } from "../context-element/event/event";
14
15
  import type { ArcEventInstance } from "../context-element/event/instance";
@@ -28,6 +29,7 @@ export interface StreamingQueryCacheStore<Item extends {
28
29
  export declare class StreamingQueryCache {
29
30
  private stores;
30
31
  private views;
32
+ private activeStreams;
31
33
  /**
32
34
  * Register views that this cache will handle
33
35
  */
@@ -38,6 +40,21 @@ export declare class StreamingQueryCache {
38
40
  getStore<Item extends {
39
41
  _id: string;
40
42
  }>(viewName: string): StreamingQueryCacheStore<Item>;
43
+ /**
44
+ * Check if a stream is already active for a view
45
+ */
46
+ hasActiveStream(viewName: string): boolean;
47
+ /**
48
+ * Register an active stream for a view (increment ref count if exists)
49
+ * Returns a function to unregister when done
50
+ */
51
+ registerStream(viewName: string, createStream: () => {
52
+ unsubscribe: () => void;
53
+ }): () => void;
54
+ /**
55
+ * Unregister from a stream (decrement ref count, close if zero)
56
+ */
57
+ private unregisterStream;
41
58
  /**
42
59
  * Set initial data for a view (from SSE stream)
43
60
  */
@@ -50,7 +67,7 @@ export declare class StreamingQueryCache {
50
67
  */
51
68
  applyEvent(event: ArcEventInstance<ArcEventAny>): Promise<void>;
52
69
  /**
53
- * Clear all cached data
70
+ * Clear all cached data and close all streams
54
71
  */
55
72
  clear(): void;
56
73
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc",
3
3
  "type": "module",
4
- "version": "0.3.2",
4
+ "version": "0.3.3",
5
5
  "private": false,
6
6
  "author": "Przemysław Krasiński [arcote.tech]",
7
7
  "description": "Arc framework core rewrite with improved event emission and type safety",