@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
|
|
3780
|
+
const cacheUnsub = store.subscribe(() => {
|
|
3751
3781
|
executeQuery();
|
|
3752
3782
|
});
|
|
3753
|
-
unsubscribers.push(
|
|
3754
|
-
const
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
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