@@ -8,6 +8,7 @@ import 'dart:convert';
88import 'dart:js_interop' ;
99
1010import 'package:async/async.dart' ;
11+ import 'package:collection/collection.dart' ;
1112import 'package:http/browser_client.dart' ;
1213import 'package:logging/logging.dart' ;
1314import 'package:powersync_core/powersync_core.dart' ;
@@ -45,15 +46,20 @@ class _SyncWorker {
4546 });
4647 }
4748
48- _SyncRunner referenceSyncTask (String databaseIdentifier, SyncOptions options,
49- String schemaJson, _ConnectedClient client) {
49+ _SyncRunner referenceSyncTask (
50+ String databaseIdentifier,
51+ SyncOptions options,
52+ String schemaJson,
53+ List <SubscribedStream > subscriptions,
54+ _ConnectedClient client) {
5055 return _requestedSyncTasks.putIfAbsent (databaseIdentifier, () {
5156 return _SyncRunner (databaseIdentifier);
5257 })
5358 ..registerClient (
5459 client,
5560 options,
5661 schemaJson,
62+ subscriptions,
5763 );
5864 }
5965}
@@ -90,13 +96,20 @@ class _ConnectedClient {
9096 },
9197 );
9298
93- _runner = _worker.referenceSyncTask (request.databaseName,
94- recoveredOptions, request.schemaJson, this );
99+ _runner = _worker.referenceSyncTask (
100+ request.databaseName,
101+ recoveredOptions,
102+ request.schemaJson,
103+ request.subscriptions? .toDart ?? const [],
104+ this ,
105+ );
95106 return (JSObject (), null );
96107 case SyncWorkerMessageType .abortSynchronization:
97108 _runner? .disconnectClient (this );
98109 _runner = null ;
99110 return (JSObject (), null );
111+ case SyncWorkerMessageType .updateSubscriptions:
112+ return (JSObject (), null );
100113 default :
101114 throw StateError ('Unexpected message type $type ' );
102115 }
@@ -137,9 +150,10 @@ class _SyncRunner {
137150 final StreamGroup <_RunnerEvent > _group = StreamGroup ();
138151 final StreamController <_RunnerEvent > _mainEvents = StreamController ();
139152
140- StreamingSync ? sync ;
153+ StreamingSyncImplementation ? sync ;
141154 _ConnectedClient ? databaseHost;
142- final connections = < _ConnectedClient > [];
155+ final connections = < _ConnectedClient , List <SubscribedStream >> {};
156+ List <SubscribedStream > currentStreams = [];
143157
144158 _SyncRunner (this .identifier) {
145159 _group.add (_mainEvents.stream);
@@ -152,8 +166,9 @@ class _SyncRunner {
152166 : final client,
153167 : final options,
154168 : final schemaJson,
169+ : final subscriptions,
155170 ):
156- connections. add ( client) ;
171+ connections[ client] = subscriptions ;
157172 final (newOptions, reconnect) = this .options.applyFrom (options);
158173 this .options = newOptions;
159174 this .schemaJson = schemaJson;
@@ -165,6 +180,8 @@ class _SyncRunner {
165180 sync ? .abort ();
166181 sync = null ;
167182 await _requestDatabase (client);
183+ } else {
184+ reindexSubscriptions ();
168185 }
169186 case _RemoveConnection (: final client):
170187 connections.remove (client);
@@ -191,6 +208,12 @@ class _SyncRunner {
191208 } else {
192209 await _requestDatabase (newHost);
193210 }
211+ case _ClientSubscriptionsChanged (
212+ : final client,
213+ : final subscriptions
214+ ):
215+ connections[client] = subscriptions;
216+ reindexSubscriptions ();
194217 }
195218 } catch (e, s) {
196219 _logger.warning ('Error handling $event ' , e, s);
@@ -199,12 +222,22 @@ class _SyncRunner {
199222 });
200223 }
201224
225+ /// Updates [currentStreams] to the union of values in [connections] .
226+ void reindexSubscriptions () {
227+ final before = currentStreams.toSet ();
228+ final after = connections.values.flattenedToSet;
229+ if (! const SetEquality <SubscribedStream >().equals (before, after)) {
230+ currentStreams = after.toList ();
231+ sync ? .updateSubscriptions (currentStreams);
232+ }
233+ }
234+
202235 /// Pings all current [connections] , removing those that don't answer in 5s
203236 /// (as they are likely closed tabs as well).
204237 ///
205238 /// Returns the first client that responds (without waiting for others).
206239 Future <_ConnectedClient ?> _collectActiveClients () async {
207- final candidates = connections.toList ();
240+ final candidates = connections.keys. toList ();
208241 if (candidates.isEmpty) {
209242 return null ;
210243 }
@@ -269,6 +302,7 @@ class _SyncRunner {
269302 );
270303 }
271304
305+ currentStreams = connections.values.flattenedToSet.toList ();
272306 sync = StreamingSyncImplementation (
273307 adapter: WebBucketStorage (database),
274308 schemaJson: client._runner! .schemaJson,
@@ -283,20 +317,21 @@ class _SyncRunner {
283317 options: options,
284318 client: BrowserClient (),
285319 identifier: identifier,
320+ activeSubscriptions: currentStreams,
286321 );
287322 sync ! .statusStream.listen ((event) {
288323 _logger.fine ('Broadcasting sync event: $event ' );
289- for (final client in connections) {
324+ for (final client in connections.keys ) {
290325 client.channel.notify (SyncWorkerMessageType .notifySyncStatus,
291326 SerializedSyncStatus .from (event));
292327 }
293328 });
294329 sync ! .streamingSync ();
295330 }
296331
297- void registerClient (
298- _ConnectedClient client, SyncOptions options, String schemaJson ) {
299- _mainEvents.add (_AddConnection (client, options, schemaJson));
332+ void registerClient (_ConnectedClient client, SyncOptions options,
333+ String schemaJson, List < SubscribedStream > subscriptions ) {
334+ _mainEvents.add (_AddConnection (client, options, schemaJson, subscriptions ));
300335 }
301336
302337 /// Remove a client, disconnecting if no clients remain..
@@ -308,6 +343,11 @@ class _SyncRunner {
308343 void disconnectClient (_ConnectedClient client) {
309344 _mainEvents.add (_DisconnectClient (client));
310345 }
346+
347+ void updateClientSubscriptions (
348+ _ConnectedClient client, List <SubscribedStream > subscriptions) {
349+ _mainEvents.add (_ClientSubscriptionsChanged (client, subscriptions));
350+ }
311351}
312352
313353sealed class _RunnerEvent {}
@@ -316,8 +356,10 @@ final class _AddConnection implements _RunnerEvent {
316356 final _ConnectedClient client;
317357 final SyncOptions options;
318358 final String schemaJson;
359+ final List <SubscribedStream > subscriptions;
319360
320- _AddConnection (this .client, this .options, this .schemaJson);
361+ _AddConnection (
362+ this .client, this .options, this .schemaJson, this .subscriptions);
321363}
322364
323365final class _RemoveConnection implements _RunnerEvent {
@@ -332,6 +374,13 @@ final class _DisconnectClient implements _RunnerEvent {
332374 _DisconnectClient (this .client);
333375}
334376
377+ final class _ClientSubscriptionsChanged implements _RunnerEvent {
378+ final _ConnectedClient client;
379+ final List <SubscribedStream > subscriptions;
380+
381+ _ClientSubscriptionsChanged (this .client, this .subscriptions);
382+ }
383+
335384final class _ActiveDatabaseClosed implements _RunnerEvent {
336385 const _ActiveDatabaseClosed ();
337386}
0 commit comments