@verdant-web/store 2.0.5 → 2.2.0

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 (39) hide show
  1. package/dist/cjs/DocumentManager.js +4 -4
  2. package/dist/cjs/DocumentManager.js.map +1 -1
  3. package/dist/cjs/openDocumentDatabase.js +5 -5
  4. package/dist/cjs/openDocumentDatabase.js.map +1 -1
  5. package/dist/cjs/queries2/GetQuery.js +1 -1
  6. package/dist/cjs/queries2/GetQuery.js.map +1 -1
  7. package/dist/cjs/reactives/Entity.d.ts +0 -1
  8. package/dist/cjs/reactives/Entity.js +4 -6
  9. package/dist/cjs/reactives/Entity.js.map +1 -1
  10. package/dist/cjs/sync/PresenceManager.d.ts +2 -0
  11. package/dist/cjs/sync/PresenceManager.js +9 -0
  12. package/dist/cjs/sync/PresenceManager.js.map +1 -1
  13. package/dist/cjs/sync/Sync.d.ts +12 -2
  14. package/dist/cjs/sync/Sync.js +24 -13
  15. package/dist/cjs/sync/Sync.js.map +1 -1
  16. package/dist/esm/DocumentManager.js +4 -4
  17. package/dist/esm/DocumentManager.js.map +1 -1
  18. package/dist/esm/openDocumentDatabase.js +5 -5
  19. package/dist/esm/openDocumentDatabase.js.map +1 -1
  20. package/dist/esm/queries2/GetQuery.js +1 -1
  21. package/dist/esm/queries2/GetQuery.js.map +1 -1
  22. package/dist/esm/reactives/Entity.d.ts +0 -1
  23. package/dist/esm/reactives/Entity.js +4 -6
  24. package/dist/esm/reactives/Entity.js.map +1 -1
  25. package/dist/esm/sync/PresenceManager.d.ts +2 -0
  26. package/dist/esm/sync/PresenceManager.js +9 -0
  27. package/dist/esm/sync/PresenceManager.js.map +1 -1
  28. package/dist/esm/sync/Sync.d.ts +12 -2
  29. package/dist/esm/sync/Sync.js +24 -13
  30. package/dist/esm/sync/Sync.js.map +1 -1
  31. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +2 -2
  34. package/src/DocumentManager.ts +4 -6
  35. package/src/openDocumentDatabase.ts +4 -5
  36. package/src/queries2/GetQuery.ts +1 -1
  37. package/src/reactives/Entity.ts +2 -9
  38. package/src/sync/PresenceManager.ts +10 -0
  39. package/src/sync/Sync.ts +36 -15
package/src/sync/Sync.ts CHANGED
@@ -120,13 +120,19 @@ export interface ServerSyncOptions<Profile = any, Presence = any>
120
120
  * HTTP push/pull to sync changes. If another user joins, both users will
121
121
  * be upgraded to websockets.
122
122
  *
123
+ * Provide `peers-only` to only automatically use websockets if other
124
+ * users connect, but not if another device for the current user connects.
125
+ * By default, automatic transport selection will upgrade to websockets if
126
+ * another device from the current user connects, but if realtime sync is
127
+ * not necessary for such cases, you can save bandwidth by disabling this.
128
+ *
123
129
  * Turning off this feature allows you more control over the transport
124
130
  * which can be useful for low-power devices or to save server traffic.
125
131
  * To modify transport modes manually, utilize `client.sync.setMode`.
126
132
  * The built-in behavior is essentially switching modes based on
127
133
  * the number of peers detected by client.sync.presence.
128
134
  */
129
- automaticTransportSelection?: boolean;
135
+ automaticTransportSelection?: boolean | 'peers-only';
130
136
  initialTransport?: SyncTransportMode;
131
137
  autoStart?: boolean;
132
138
  /**
@@ -148,7 +154,9 @@ export interface ServerSyncOptions<Profile = any, Presence = any>
148
154
  }
149
155
 
150
156
  export class ServerSync<Profile = any, Presence = any>
151
- extends EventSubscriber<SyncEvents>
157
+ extends EventSubscriber<
158
+ SyncEvents & { syncingChange: (syncing: boolean) => void }
159
+ >
152
160
  implements Sync
153
161
  {
154
162
  private webSocketSync: WebSocketSync;
@@ -162,6 +170,7 @@ export class ServerSync<Profile = any, Presence = any>
162
170
  reset?: boolean;
163
171
  }) => Promise<void>;
164
172
  private broadcastChannel: BroadcastChannel | null = null;
173
+ private _activelySyncing = false;
165
174
 
166
175
  private meta: Metadata;
167
176
 
@@ -250,30 +259,34 @@ export class ServerSync<Profile = any, Presence = any>
250
259
  this.pushPullSync.subscribe('message', this.handleMessage);
251
260
  this.pushPullSync.subscribe('onlineChange', this.handleOnlineChange);
252
261
 
253
- if (automaticTransportSelection) {
262
+ if (automaticTransportSelection && this.canDoRealtime) {
254
263
  // automatically shift between transport modes depending
255
264
  // on whether any peers are present
256
- let switchoverTimeout: NodeJS.Timer;
257
- this.presence.subscribe('peersChanged', (peers) => {
265
+ const decideIfUpgrade = () => {
258
266
  if (switchoverTimeout) {
259
267
  clearTimeout(switchoverTimeout);
260
268
  }
261
- if (Object.keys(peers).length > 0) {
262
- // only upgrade if token allows it
263
- if (this.canDoRealtime) {
264
- if (this.mode === 'pull') {
265
- this.setMode('realtime');
266
- }
267
- }
268
- } else if (this.mode === 'realtime') {
269
- // wait 1 second then switch to pull mode if still emtpy
269
+ const hasPeers = Object.keys(this.presence.peers).length > 0;
270
+ const shouldUpgrade =
271
+ hasPeers ||
272
+ (automaticTransportSelection !== 'peers-only' &&
273
+ this.presence.selfReplicaIds.size > 1);
274
+ if (shouldUpgrade && this.mode === 'pull') {
275
+ this.setMode('realtime');
276
+ } else if (!shouldUpgrade && this.mode === 'realtime') {
277
+ // wait 1 second then switch to pull mode if still empty
270
278
  switchoverTimeout = setTimeout(() => {
271
279
  if (Object.keys(this.presence.peers).length === 0) {
272
280
  this.setMode('pull');
273
281
  }
274
282
  }, 1000);
275
283
  }
276
- });
284
+ };
285
+ let switchoverTimeout: NodeJS.Timer;
286
+ this.presence.subscribe('peersChanged', decideIfUpgrade);
287
+ if (automaticTransportSelection !== 'peers-only') {
288
+ this.presence.subscribe('selfChanged', decideIfUpgrade);
289
+ }
277
290
  }
278
291
 
279
292
  if (autoStart) {
@@ -289,6 +302,10 @@ export class ServerSync<Profile = any, Presence = any>
289
302
  );
290
303
  }
291
304
 
305
+ get syncing() {
306
+ return this._activelySyncing;
307
+ }
308
+
292
309
  private handleBroadcastChannelMessage = (event: MessageEvent) => {
293
310
  if (event.data.type === 'sync') {
294
311
  this.handleMessage(event.data.message);
@@ -318,6 +335,8 @@ export class ServerSync<Profile = any, Presence = any>
318
335
  await this.meta.setGlobalAck(message.timestamp);
319
336
  break;
320
337
  case 'sync-resp':
338
+ this._activelySyncing = true;
339
+ this.emit('syncingChange', true);
321
340
  await this.onData({
322
341
  operations: message.operations,
323
342
  baselines: message.baselines,
@@ -329,6 +348,8 @@ export class ServerSync<Profile = any, Presence = any>
329
348
  }
330
349
 
331
350
  await this.meta.updateLastSynced(message.ackedTimestamp);
351
+ this._activelySyncing = false;
352
+ this.emit('syncingChange', false);
332
353
  break;
333
354
  case 'need-since':
334
355
  this.activeSync.send(